No exported symbols with es6 modules library compiled by Closure Compiler

不想你离开。 提交于 2019-12-11 07:00:48

问题


Starting point: Many js files are successfully compiled (no warning/error) by Closure Compiler (ADVANCED_OPTIMIZATIONS level) in a single library file.

In these js files:

  • goog.require and goog.provide are used to import/export things between them
  • /** @export */ is used in front of whatever (const/function/Class/var) is required outside the library.

Some HTML files include the library and some non compiled js accessing successfully to all whatever defined in this library.

What I want: move to es6 module syntax

What I did for each js file:

  • goog.require replaced by import with the list of Class, function from another js file
  • goog.provide removed and export added in front of each Class, function etc. required by another js file
  • Try 1: no change for the /** @export */ each time whatever is required outside the library.
  • Try 2: all /** @export */ whatever replaced by goog.exportSymbol('whatever', whatever)

This is sucessfully compiled (no warning/error, still with ADVANCED_OPTIMIZATIONS level).

The problem: now, for the same HTML files, all the whatever defined in the library are seen "undefined" by the browser. Indeed, when I type Object.keys( window ) in the console, I can see all the symbol names changed by the compiler (aa, ba, ca etc.) but none of my exported symbol whatever.

Example: demoVisitors is a const array defined in the library and required outside. Before in the library file, I could see ... w("demoVisitors",[Oa,La,Ma,Na]); ... and the content was properly visible in the HTML page. After the es6 module change, I can see ... H("demoVisitors$$module$filemane",Oa); ... (filename being the file name in which demoVisitors is defined) for try 1 and H("demoVisitors",[Na,Ka,La,Ma]); for try 2. demoVisitors is undefined in the browser for the same page.


回答1:


After further investigations, I found the solution.

Although loaded in the browser without any error in the console (except undefined whatever of course), my library was not executed. I simply moved the closure library ahead of the file stack to be compiled and my library was then properly executed by the browser with my symbols properly exported. See below for more details.

The 3 ways to export symbols are working in compiled es6 modules: /** @export */ whatever, goog.exportSymbol('whatever', whatever), window['whatever'] = whatever. The first 2 being a handy way for the third one (for root symbols).

Nevertheless /** @export */ myClass generates an unfriendly unobfuscated names like myClass$$module$source-filename-with-path. To get the unobfuscated name myClass, avoid a goog function within my code and enable neatly both compiled/uncompiled mode, I removed the /** @export */ and add unobfuscateSymbol('myClass', myClass) after class myClass { ... }. It's my "own" function directly inspired from exportSymbol function defined in the closure library. This is only required for root symbols like classes, you can keep /** @export */ for all the symbols (properties, functions etc.) defined in the class.

Here is the source code:

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];
            }
        }
    }
}

How I identified the issue in details:

  1. To understand the export issue, I tried to put a breakpoint in the exportSymbol function defined in the closure library while loading my HTML test page in the browser: no break...

  2. I double checked this execution issue by adding a console.log("my library is being executed"): I was able to see the message in the goog.require/goog.provide version of my library but not in the es6 import/export version. Without execution, of course, no symbol exported.

  3. I wrapped my library with a IIFE. Closure compiler argument: --output_wrapper "(function(){%output%})()" and an execution error message in my library appeared in the browser console. I discovered that goog.global, the base namespace for the Closure library, was undefined at the break time.

  4. I moved the closure library ahead of the file stack to be compiled. Closure compiler arguments: –js closure-lib-path/base.js –js myfile1.js –js myfile2.js … to make sure compiled goog stuff is before the first exportSymbol call generated by the first /** @export */ compiled.

  5. Execution and exports were OK then in the browser. I just added the unobfuscateSymbol function as described above to get the same friendly exported names as for the goog.require/goog.provide version of my library.



来源:https://stackoverflow.com/questions/46092308/no-exported-symbols-with-es6-modules-library-compiled-by-closure-compiler

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!