rules_closure icon indicating copy to clipboard operation
rules_closure copied to clipboard

Weird issues with externs

Open sgammon opened this issue 3 years ago • 2 comments

When trying to use Google Maps externs, I get some very strange behavior that doesn't seem to have a remedy except for a bug fix.

consider the following property initializer in an arbitrary JS class and accompanying Bazel targets:

WORKSPACE:

# ...

http_file(
    name = "com_google_gmaps",
    downloaded_file_path = "gmaps-extern.js",
    urls = ["https://raw.githubusercontent.com/google/closure-compiler/db22839eee034c1b302879f64a2984407459b0f2/contrib/externs/maps/google_maps_api_v3_43.js"],
)

# ...

BUILD.bazel:

closure_js_library(
    name = "gmaps",
    srcs = ["@com_google_gmaps//file"],
)

closure_js_library(
    name = "some-thing",
    srcs = ["some-thing.js"],
    deps = [":gmaps"],
)

closure_js_binary(
    name = "some-js",
    deps = [":some-thing"],
    dependency_mode = "PRUNE_LEGACY",
    entry_points = ["repro.some_thing"],
)

some-thing.js:

goog.module('repro.some_thing');

class SomeThing {
    constructor() {
      /**
       * Holds a reference to the managed Maps marker.
       *
       * @const
       * @type {!google.maps.Marker}
       * @private
       */
      this.marker_ = new google.maps.Marker({});
    }
}

simple, right? however, Closure Compiler fails to recognize the symbols as externs:

(17:00:49) INFO: Current date is 2021-01-16
(17:00:49) INFO: Analyzed target //:some-js.js (0 packages loaded, 0 targets configured).
(17:00:49) INFO: Found 1 target...
(17:00:56) ERROR: /...project path.../BUILD.bazel:207:7: Compiling N JavaScript files to some-js.js failed (Exit 1): ClosureWorker failed: error executing command bazel-out/host/bin/external/io_bazel_rules_closure/java/io/bazel/rules/closure/ClosureWorker @@bazel-out/darwin-fastbuild/bin/some-js.js-0.params
some-thing.js:12: ERROR - could not determine the type of this expression
    this.marker_ = new google.maps.Marker({});
                       ^
  ProTip: "JSC_UNKNOWN_EXPR_TYPE" or "missingSourcesWarnings" or "reportUnknownTypes" can be added to the `suppress` attribute of:
  //:some-thing
  Alternatively /** @suppress {reportUnknownTypes} */ can be added to the source file.

1 error(s), 0 warning(s), 97.6% typed

Target //:some-js failed to build
Use --verbose_failures to see the command lines of failed build steps.
(17:00:56) INFO: Elapsed time: 6.756s, Critical Path: 6.29s
(17:00:56) INFO: 3 processes: 2 internal, 1 worker.
(17:00:56) FAILED: Build did NOT complete successfully

Diagnosis

Okay, so, let's start in the extern (google_maps_api_v3_43.js).

google_maps_api_v3_43.js:4845:

/**
 * @param {google.maps.MarkerOptions=} opts
 * @extends {google.maps.MVCObject}
 * @constructor
 */
google.maps.Marker = function(opts) {};

So it's in the extern. So maybe the extern isn't getting loaded by rules_closure / JsChecker? Let's check:

> grep gmaps bazel-out/darwin-fastbuild/bin/some-js.js-0.params
bazel-out/darwin-fastbuild/bin/tools/externs/libs/gmaps.pbtxt
tools/externs/libs/gmaps.js

So it's making it into the build ☹️

To make matters even more interesting, with the Firebase externs, I had to change the firebase = {}; @namespace to const = for it to work:

firebase-externs.js:

/**
 * @fileoverview Firebase namespace and Firebase App API.
 * @externs
 */

/**
 * <code>firebase</code> is a global namespace from which all the Firebase
 * services are accessed.
 *
 * @namespace
 */
const firebase = {};

No idea why, and I'm still suspicious of this change (i.e. maybe it just busted a build cache somewhere), but changing var = to const = fixed my extern issues with Firebase. Trying that with Google Maps wasn't fruitful:

google_maps_api_v3_43.js:4845:

/**
 * @fileoverview Externs for the Google Maps v3 API.
 * @see https://developers.google.com/maps/documentation/javascript/reference
 * @externs
 */

/**
 * @const
 * @suppress {const,duplicate,strictMissingProperties}
 */
const google = {};

Yields:

(17:17:02) INFO: Current date is 2021-01-16
(17:17:02) INFO: Analyzed target //:some-js.js (0 packages loaded, 0 targets configured).
(17:17:02) INFO: Found 1 target...
(17:17:08) ERROR: /Volumes/VANTAGE/platform/cookies/frontend/BUILD.bazel:207:7: Compiling N JavaScript files to some-js.js failed (Exit 1): ClosureWorker failed: error executing command bazel-out/host/bin/external/io_bazel_rules_closure/java/io/bazel/rules/closure/ClosureWorker @@bazel-out/darwin-fastbuild/bin/some-js.js-0.params
(gmaps-extern):27: ERROR - Illegal redeclared variable: google
const google = {};
      ^
  ProTip: "JSC_REDECLARED_VARIABLE_ERROR" can be added to the `suppress` attribute of:
  //:gmaps

1 error(s), 0 warning(s)

Target //:some-js.js failed to build
Use --verbose_failures to see the command lines of failed build steps.
(17:18:26) INFO: Elapsed time: 1.793s, Critical Path: 1.33s
(17:18:26) INFO: 3 processes: 2 internal, 1 worker.
(17:18:26) FAILED: Build did NOT complete successfully

This is already weird because I don't have a google = anywhere in my code (in an extern or otherwise). As far as I can tell, none of my upstream dependencies do, either. But if I add that suppression:

# ...
ERROR: Bad --suppress value: JSC_REDECLARED_VARIABLE_ERROR
# ...

Going back to our Google Maps sample, what's even more interesting is, it will find certain symbols in the gmaps extern, for instance:

  • google.maps.LatLng
  • google.maps.Point
  • google.maps.Map (it seems to find this sometimes)

So, I'm stuck at this point, because (1) there appears to (maybe) be a builtin google that I cannot re-define, because of (2) a warning from JSC that I cannot suppress or (3) perhaps an inability to re-declare this namespace for my use.

Any ideas? Is anyone else experiencing the same thing?

sgammon avatar Jan 17 '21 01:01 sgammon

there appears to (maybe) be a builtin google that I cannot re-define

Could you reproduce the issue without rules_closure and file a bug with Closure Compiler?

a warning from JSC that I cannot suppress

IIRC, rules_closure doesn't let you suppress, and I'm not sure what criteria used there but this warning sounds something that you don't want to suppress.

gkdn avatar Jan 20 '21 20:01 gkdn

I had the same issue today and even that the cause of the problem was not found, the solution was to use --env=CUSTOM for the compiler, so the default externs are disabled. Then adding only the externs that were needed.

Here is the valuable issue #81 that gave me the hint for this solution.

gramic avatar May 19 '23 16:05 gramic