bazelbuild / bazelbuild/rules_closure
Weird issues with externs
- Dominant language
- Java
- Stars
- 159
- Forks
- 111
- PR merge metrics
- No merged PRs in 30d
Description
When trying to use [Google Maps externs](https://github.com/google/closure-compiler/blob/master/contrib/externs/maps/google_maps_api_v3_43.js), 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`**:
```starlark
# ...
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`**:
```starlark
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`:**
```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`)](https://github.com/google/closure-compiler/blob/master/contrib/externs/maps/google_maps_api_v3_43.js).
**`google_maps_api_v3_43.js:4845`:**
```js
/**
* @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`:**
```js
/**
* @fileoverview Firebase namespace and Firebase App API.
* @externs
*/
/**
* firebase 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?
Contributor guide
Research direction
Start with the WORKSPACE and BUILD.bazel reproducer, then inspect some-thing.js and the referenced google_maps_api_v3_43.js extern. Run the Bazel target and compare the generated some-js.js-0.params entries with JsChecker's diagnostics. Done means the extern symbols resolve consistently without the redeclaration or unknown-type errors.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- javascript
- Domain
- build-system, tooling
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 32/100