简体   繁体   中英

Prevent Closure Compiler from renaming properties without using bracket notation

I'm working on a product which needs to conditionally expose an API to the end user. We're using the Closure Compiler for minification. The part of the API that I'm trying to expose is a function that calls a callback with a Result whose value is an object with certain properties.

Specifically, the function I'm trying to expose looks like this (in pseudo-jsdoc notation):

/**
 * @type DocumentRenderResult {status: String, image?: HTMLImageElement|HTMLCanvasElement}
 * @param {function(Result<DocumentRenderResult>)} callback
**/
function renderDocument (url, page, callback) {

}

And the Result class looks like this:

/**
 * @template T
 * @param {Boolean} ok
 * @param {T} val
 * @param {Error} err
**/
function Result (ok, val, err) {
    this.ok = ok;
    this.val = val;
    this.err = err;
}

What I'd like is to expose both the Result object API - that is, the fact that there are ok , val , and err properties, and expose the renderDocument interface so that users providing a callback will be able to access the status and image properties.

One solution (possibly) is to use bracket notation everywhere, but since this is supposed to be only conditionally exposed (for certain end users), I'd like to separate the concept of whether it's minified or not from the code.

I think some combination of @implements and @export and an externs file can accomplish this, but I haven't figured it out.

Is what I'm trying to do possible?

There are 2 primary ways to handle this situation:

Without Closure-Library

Store all of your exports in a separate file that becomes the main entry point for the api exposed library. This methodology has no dependencies on external libraries. For an example of this, see my GeolocationMarker library .

With Closure-Library (or at least a part of it)

If you are willing to utilize a very small part of closure-library code in your project, you can use the @export annotation. @export annotations do nothing unless the compilation flag --generate_exports is specified.

When --generate_exports is used, the compiler will add the appropriate call to either closure-library's goog.exportProperty or goog.exportSymbol method.

/** @const */
var mynamespace = {};

/** @export */
mynamespace.maybeExposedMethod = function() {};

With the --generate_exports flag, the compiler will generate the code:

var mynamespace = {maybeExposedMethod:function() {}};
goog.exportSymbol("mynamespace.maybeExposedMethod",
    mynamespace.maybeExposedMethod);

Instead of having to depend on all of closure-library. You can copy the definition for those 2 methods into your source. All that's needed is that they exist as those names.

Conditionally Renaming Record Types

Functions which return anonymous objects must be treated differently to prevent renaming. The best option here would be to define a record type in its own file. For the public api to block renaming, include the record definition as an extern. When you want the record properties to be renamed, include the definition as source.

my_typedefs.js

/** @typedef {{image: string, status: number}} */
var my_record_type;

my_method_def.js

/** @return {my_record_type} */
function my_method() {
  return {
    image: 'some_image.jpg',
    status: 200
  };
}

If my_typedefs.js is included in the compilation with the --js flag, the record properties will be renamed, but if it is included with the --externs flag, they will not be renamed.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM