rules_closure
rules_closure copied to clipboard
Weird issues with externs
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?
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.
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.