Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Deck.gl fails to initialize when run with AOT compilation with Angular #3567

Closed
SterlingMcCall opened this issue Sep 11, 2019 · 23 comments
Closed
Labels

Comments

@SterlingMcCall
Copy link

SterlingMcCall commented Sep 11, 2019

Description

I am using the Deck.gl GoogleMapsOverlay with a Google map in an Angular application. It works just fine with the JIT compilation used most commonly by angular during development, but when the application is run with AOT compilation w/ Webpack as one would in a production environment, the resulting bundles don't load deck.gl correctly and all I see is the default google map. I believe it may be a treeshaking bug similar to #3213.

My current work-around is to load it via the CDN link (https://unpkg.com/deck.gl@latest/dist.min.js) but that increases the bundle size considerably and breaks all IDE code suggestion support and whatnot.

Repro Steps

I created a simple angular project which demonstrates the issue. Refer to the README to get running quickly.
https://github.com/SterlingMcCall/angular-deck-gl-error-demo

Environment:

  • Framework Version: deck.gl 7.2.3 and earlier, 7.3.0-alpha.6
  • Browser Version: Chrome, Firefox
  • Webpack: tested with versions 4.19.1, 4.23.1, and 4.39.3

Logs

Unhandled Promise rejection: TIMER_QUERY ; Zone: <root> ; Task: Promise.then ; Value: Error: TIMER_QUERY
    at r (assert.js.pre-build-optimizer.js:3)
    at c (features.js.pre-build-optimizer.js:30)
    at features.js.pre-build-optimizer.js:10
    at Array.every (<anonymous>)
    at s (features.js.pre-build-optimizer.js:9)
    at Function.value (query.js.pre-build-optimizer.js:27)
    at animation-loop.js.pre-build-optimizer.js:147
    at e.invoke (zone.js.pre-build-optimizer.js:391)
    at t.run (zone.js.pre-build-optimizer.js:150)
    at zone.js.pre-build-optimizer.js:910 Error: TIMER_QUERY
    at r (http://localhost:4201/vendor.6ff8643349f59a82f6c2.js:1:211879)
    at c (http://localhost:4201/vendor.6ff8643349f59a82f6c2.js:1:494543)
    at http://localhost:4201/vendor.6ff8643349f59a82f6c2.js:1:494339
    at Array.every (<anonymous>)
    at s (http://localhost:4201/vendor.6ff8643349f59a82f6c2.js:1:494314)
    at Function.value (http://localhost:4201/vendor.6ff8643349f59a82f6c2.js:1:384784)
    at http://localhost:4201/vendor.6ff8643349f59a82f6c2.js:1:613204
    at e.invoke (http://localhost:4201/polyfills.42fc305c63116b2cde64.js:1:7375)
    at t.run (http://localhost:4201/polyfills.42fc305c63116b2cde64.js:1:2578)
    at http://localhost:4201/polyfills.42fc305c63116b2cde64.js:1:14554
@Pessimistress
Copy link
Collaborator

I suspect that this has to do with tree shaking again. Is there a way you can inspect the compiled code to see if @luma.gl/webgl-polyfill is included?

https://github.com/uber/luma.gl/blob/master/modules/webgl/src/init.js#L8

@ibgreen
Copy link
Collaborator

ibgreen commented Sep 11, 2019

I like the clean example... If we get this example working, maybe it could be contributed as a get-started example for angular users?

@SterlingMcCall
Copy link
Author

SterlingMcCall commented Sep 11, 2019

@Pessimistress
In the vendor bundle in the code source mapped to webpack:///./node_modules/@luma.gl/webgl/dist/esm/init.js.pre-build-optimizer.js I can see that it does contain import '@luma.gl/webgl2-polyfill';. Source mapping is also able to identify that the file webpack:///./node_modules/@luma.gl/webgl2-polyfill/dist/esm/polyfill-table.js.pre-build-optimizer.js is present in the vendor bundle. I'm not really sure if that answers your question.

@ibgreen
Feel free to create any examples derived from my repo.

@Pessimistress
Copy link
Collaborator

Looks like the default export of this file: https://unpkg.com/browse/@luma.gl/webgl@7.2.0/dist/esm/features/webgl-features-table.js

is an empty object in prod mode. I replaced the transpiled code with source and it worked. @SterlingMcCall are you able to resolve modules to the es6 end point (esnext in package.json) instead of esm (module)?

@SterlingMcCall
Copy link
Author

SterlingMcCall commented Sep 12, 2019

I'm not really sure what you are asking me to do in package.json. Adding "module": "esnext" doesn't have any effect, and I wouldn't expect it to based on the definition of the module property.

I did try changing all the package.json files in /node_modules/@deck.gl's and /node_modules/@luma.gl's sub-modules to have "main": "dist/es6/index.js", "module": "dist/es6/index.js" instead of "main": "dist/es5/index.js", "module": "dist/esm/index.js" which did result in the different errors (included below) like those I was getting in deck.gl 7.1.x and is similar to #3213. I also tried adding "target": "esnext", "module": "esnext" to compilerOptions in tsconfig.json to no avail.

layer-manager.js.pre-build-optimizer.js:312 deck: error while initializing BitmapLayer({id: 'bitmap-layer'})
 Error: Unknown shader module project32
    at r (assert.js.pre-build-optimizer.js:3)
    at s.getShaderModule (shader-module-registry.js.pre-build-optimizer.js:39)
    at shader-module-registry.js.pre-build-optimizer.js:46
    at Array.map (<anonymous>)
    at s.resolveModules (shader-module-registry.js.pre-build-optimizer.js:46)
    at l (resolve-modules.js.pre-build-optimizer.js:19)
    at E (assemble-shaders.js.pre-build-optimizer.js:46)
    at ee._createProgram (base-model.js.pre-build-optimizer.js:219)
    at ee.initialize (base-model.js.pre-build-optimizer.js:26)
    at ee.initialize (model.js.pre-build-optimizer.js:19)
_initializeLayer @ layer-manager.js.pre-build-optimizer.js:312
_updateSublayersRecursively @ layer-manager.js.pre-build-optimizer.js:255
_updateLayers @ layer-manager.js.pre-build-optimizer.js:216
setLayers @ layer-manager.js.pre-build-optimizer.js:134
setProps @ layer-manager.js.pre-build-optimizer.js:115
setProps @ deck.js.pre-build-optimizer.js:207
_setGLContext @ deck.js.pre-build-optimizer.js:613
_onRendererInitialized @ deck.js.pre-build-optimizer.js:645
onInitialize @ animation-loop.js.pre-build-optimizer.js:214
(anonymous) @ animation-loop.js.pre-build-optimizer.js:123
e.invoke @ zone.js.pre-build-optimizer.js:391
t.run @ zone.js.pre-build-optimizer.js:150
(anonymous) @ zone.js.pre-build-optimizer.js:910
e.invokeTask @ zone.js.pre-build-optimizer.js:423
t.runTask @ zone.js.pre-build-optimizer.js:195
y @ zone.js.pre-build-optimizer.js:601
t.invokeTask @ zone.js.pre-build-optimizer.js:502
invoke @ zone.js.pre-build-optimizer.js:487
n.args.<computed> @ zone.js.pre-build-optimizer.js:3070
requestAnimationFrame (async)
c @ zone.js.pre-build-optimizer.js:3091
e.scheduleTask @ zone.js.pre-build-optimizer.js:410
t.scheduleTask @ zone.js.pre-build-optimizer.js:238
t.scheduleMacroTask @ zone.js.pre-build-optimizer.js:261
v @ zone.js.pre-build-optimizer.js:1194
(anonymous) @ zone.js.pre-build-optimizer.js:3106
F.i.<computed> @ zone.js.pre-build-optimizer.js:1518
Ts @ map.js:1
Pu @ map.js:43
(anonymous) @ map.js:46
Iu.ta @ map.js:78
_.p.ta @ map.js:81
hv.l @ map.js:89
(anonymous) @ js?v=quarterly&callback=agmLazyMapsAPILoader&key=:115
e.invoke @ zone.js.pre-build-optimizer.js:391
t.run @ zone.js.pre-build-optimizer.js:150
(anonymous) @ zone.js.pre-build-optimizer.js:910
e.invokeTask @ zone.js.pre-build-optimizer.js:423
t.runTask @ zone.js.pre-build-optimizer.js:195
y @ zone.js.pre-build-optimizer.js:601
Promise.then (async)
d @ zone.js.pre-build-optimizer.js:584
e.scheduleTask @ zone.js.pre-build-optimizer.js:413
t.scheduleTask @ zone.js.pre-build-optimizer.js:238
t.scheduleMicroTask @ zone.js.pre-build-optimizer.js:258
z @ zone.js.pre-build-optimizer.js:900
D @ zone.js.pre-build-optimizer.js:846
(anonymous) @ zone.js.pre-build-optimizer.js:762
(anonymous) @ js?v=quarterly&callback=agmLazyMapsAPILoader&key=:181
(anonymous) @ js?v=quarterly&callback=agmLazyMapsAPILoader&key=:76
(anonymous) @ js?v=quarterly&callback=agmLazyMapsAPILoader&key=:181
Je @ js?v=quarterly&callback=agmLazyMapsAPILoader&key=:76
Fe.ac @ js?v=quarterly&callback=agmLazyMapsAPILoader&key=:181
(anonymous) @ map.js:1
zone.js.pre-build-optimizer.js:703 Unhandled Promise rejection: Unknown shader module project32 ; Zone: <root> ; Task: Promise.then ; Value: Error: Unknown shader module project32
    at r (assert.js.pre-build-optimizer.js:3)
    at s.getShaderModule (shader-module-registry.js.pre-build-optimizer.js:39)
    at shader-module-registry.js.pre-build-optimizer.js:46
    at Array.map (<anonymous>)
    at s.resolveModules (shader-module-registry.js.pre-build-optimizer.js:46)
    at l (resolve-modules.js.pre-build-optimizer.js:19)
    at E (assemble-shaders.js.pre-build-optimizer.js:46)
    at ee._createProgram (base-model.js.pre-build-optimizer.js:219)
    at ee.initialize (base-model.js.pre-build-optimizer.js:26)
    at ee.initialize (model.js.pre-build-optimizer.js:19) Error: Unknown shader module project32
    at r (http://localhost:4201/vendor.e2b900d2e969aaf1af74.js:1:312173)
    at s.getShaderModule (http://localhost:4201/vendor.e2b900d2e969aaf1af74.js:1:168126)
    at http://localhost:4201/vendor.e2b900d2e969aaf1af74.js:1:168207
    at Array.map (<anonymous>)
    at s.resolveModules (http://localhost:4201/vendor.e2b900d2e969aaf1af74.js:1:168195)
    at l (http://localhost:4201/vendor.e2b900d2e969aaf1af74.js:1:169560)
    at E (http://localhost:4201/vendor.e2b900d2e969aaf1af74.js:1:751875)
    at ee._createProgram (http://localhost:4201/vendor.e2b900d2e969aaf1af74.js:1:274504)
    at ee.initialize (http://localhost:4201/vendor.e2b900d2e969aaf1af74.js:1:271575)
    at ee.initialize (http://localhost:4201/vendor.e2b900d2e969aaf1af74.js:1:278120)
r.onUnhandledError @ zone.js.pre-build-optimizer.js:703
p @ zone.js.pre-build-optimizer.js:730
e @ zone.js.pre-build-optimizer.js:720
r.microtaskDrainDone @ zone.js.pre-build-optimizer.js:724
y @ zone.js.pre-build-optimizer.js:608
t.invokeTask @ zone.js.pre-build-optimizer.js:502
invoke @ zone.js.pre-build-optimizer.js:487
n.args.<computed> @ zone.js.pre-build-optimizer.js:3070
requestAnimationFrame (async)
c @ zone.js.pre-build-optimizer.js:3091
e.scheduleTask @ zone.js.pre-build-optimizer.js:410
t.scheduleTask @ zone.js.pre-build-optimizer.js:238
t.scheduleMacroTask @ zone.js.pre-build-optimizer.js:261
v @ zone.js.pre-build-optimizer.js:1194
(anonymous) @ zone.js.pre-build-optimizer.js:3106
F.i.<computed> @ zone.js.pre-build-optimizer.js:1518
Ts @ map.js:1
Pu @ map.js:43
(anonymous) @ map.js:46
Iu.ta @ map.js:78
_.p.ta @ map.js:81
hv.l @ map.js:89
(anonymous) @ js?v=quarterly&callback=agmLazyMapsAPILoader&key=:115
e.invoke @ zone.js.pre-build-optimizer.js:391
t.run @ zone.js.pre-build-optimizer.js:150
(anonymous) @ zone.js.pre-build-optimizer.js:910
e.invokeTask @ zone.js.pre-build-optimizer.js:423
t.runTask @ zone.js.pre-build-optimizer.js:195
y @ zone.js.pre-build-optimizer.js:601
Promise.then (async)
d @ zone.js.pre-build-optimizer.js:584
e.scheduleTask @ zone.js.pre-build-optimizer.js:413
t.scheduleTask @ zone.js.pre-build-optimizer.js:238
t.scheduleMicroTask @ zone.js.pre-build-optimizer.js:258
z @ zone.js.pre-build-optimizer.js:900
D @ zone.js.pre-build-optimizer.js:846
(anonymous) @ zone.js.pre-build-optimizer.js:762
(anonymous) @ js?v=quarterly&callback=agmLazyMapsAPILoader&key=:181
(anonymous) @ js?v=quarterly&callback=agmLazyMapsAPILoader&key=:76
(anonymous) @ js?v=quarterly&callback=agmLazyMapsAPILoader&key=:181
Je @ js?v=quarterly&callback=agmLazyMapsAPILoader&key=:76
Fe.ac @ js?v=quarterly&callback=agmLazyMapsAPILoader&key=:181
(anonymous) @ map.js:1
component-state.js.pre-build-optimizer.js:154 TypeError: Cannot read property 'length' of undefined
    at re (props.js.pre-build-optimizer.js:187)
    at props.js.pre-build-optimizer.js:36
    at h.diffProps (layer.js.pre-build-optimizer.js:740)
    at h._onAsyncPropUpdated (layer.js.pre-build-optimizer.js:841)
    at Pe._setAsyncPropValue (component-state.js.pre-build-optimizer.js:133)
    at component-state.js.pre-build-optimizer.js:144
    at e.invoke (zone.js.pre-build-optimizer.js:391)
    at t.run (zone.js.pre-build-optimizer.js:150)
    at zone.js.pre-build-optimizer.js:910
    at e.invokeTask (zone.js.pre-build-optimizer.js:423)

@Pessimistress
Copy link
Collaborator

@SterlingMcCall It's not going into your own package.json, but your compiler configuration. I'm not familiar with Angular so I can't say what exactly you need to change. In webpack it's documented here: https://webpack.js.org/configuration/resolve/#resolvemainfields

@SterlingMcCall
Copy link
Author

SterlingMcCall commented Sep 13, 2019

@Pessimistress Thank you for the clarification.
Merging

module.exports = {
    resolve: {
        mainFields: ['esnext', 'browser', 'module', 'main']
    }
};

with the default angular webpack configuration worked and deck.gl now loads correctly.

Is this the solution or is this more of a work-around? Do you know if this impacts browser compatibility?

For anyone else in the same situation, I followed this article on how to modify the angular webpack config. I also pushed a new branch (working-with-custom-webpack) to my example repo with the working version.

@ibgreen
Copy link
Collaborator

ibgreen commented Sep 13, 2019

@SterlingMcCall The es6 distribution is the most compact and provides the best performance and debugging experience but it achieves this by intentionally limiting browser compatibility. It is built with babels preset-env with the following settings;

https://github.com/uber-web/ocular/blob/master/modules/dev-tools/config/babel.config.js#L5

const ES6_TARGETS = {
  chrome: '64', // Released: 2018-Jan-24, https://en.wikipedia.org/wiki/Google_Chrome_version_history
  edge: '18', // Released: 2018-Nov-13, https://en.wikipedia.org/wiki/Microsoft_Edge
  firefox: '60', // Released: 2018-May-9, https://en.wikipedia.org/wiki/Firefox_version_history
  safari: '12', // Released: 2018-09-07 (OSX Mojave) - https://en.wikipedia.org/wiki/Safari_version_history
  ios: '12', // Track Safari
  node: '8' // Node 8 is LTS until December 31, 2019.
};

@SterlingMcCall
Copy link
Author

@ibgreen But if I'm resolving dependencies with esnext instead of es6 wouldn't it potentially be even more restrictive?

@ibgreen
Copy link
Collaborator

ibgreen commented Sep 13, 2019

No, we define the esnext entry point to be our es6 distribution. https://github.com/uber/deck.gl/blob/master/modules/core/package.json#L21

We should probably rename the dist/es6 distribution, as preset-env supports es2018 now. Although deck.gl itself mostly sticks to pure es6, some of our other repos (like loaders.gl) are heavily es2018-based (async iterators etc) and their es6 dists will contain es2018 syntax (which is supported by all the browsers in the list).

@SterlingMcCall
Copy link
Author

@ibgreen I see. Is it possible to configure webpack to only resolve the esnext mainField for deck.gl and its dependencies like luma.gl?

@ibgreen
Copy link
Collaborator

ibgreen commented Sep 13, 2019

I don't know of any way to do that, however I also don't know of any other modules that expose the esnext field. It is not as standardized as main and module, although we did originally choose esnext because it was mentioned in some bundler-related discussions.

I guess technically we could add a unique entry to our package.json files (something like visgl-esnext: .../dist/es6) that would let you cherry pick just the es6 dist for our packages. But I'd want to see a stronger use case to make this change, it would affect all our repos.

You could also run babel on our packages and transpile them further to your taste by modifying the exclude: /node_modules/ statement in your babel rules although this would likely slow down your builds.

@ibgreen
Copy link
Collaborator

ibgreen commented Sep 13, 2019

The above discussion aside, ultimately we should fix your tree shaking issue, we just need to understand it better.

To facilitate having folks here play around with this, I think we should just add your example in its current working state to test/apps/angular directory in deck.gl.

Would you consider putting up a PR?

If we can get it working well we could move it to examples/get-started/angular.

@SterlingMcCall
Copy link
Author

@ibgreen Do you want me to make a PR for the working version with the modified webpack config or the non-working version? And my example is specific to Google maps, and more specifically uses the Angular Google Maps library to make it easier to integrate the map with Angular bindings, so it's not the most 'pure' implementation of Deck.gl with Angular. Does that matter?

@ibgreen
Copy link
Collaborator

ibgreen commented Sep 13, 2019

  • Lets add the working version with mainFields resolution for now.

  • If the angular google maps module works well and is a big official or semi-official module, then it would seem to be the setup that most angular devs would like to see in the example.

  • Similar to how we offer react-map-gl for React developers.

The main concern would be maintenance of this setup. As you probably know, we did have to do a lot of work to get google maps to integrate well with deck in our own google-maps module, here we need to deal with two complex, evolving dependencies: angular and angular-google-maps.

But we could mark the angular example as a contributor-led effort. We can provide assistance but do not guarantee to keep it working for every new version of angular and angular-google-maps.

A good README in the example summarizing some of the key takeaways from your integration effort would be most appreciated.

FYI @donmccurdy

@SterlingMcCall
Copy link
Author

@ibgreen I've been using the Angular Google Maps library for over 2 years and it's maintained relatively well. Plus, google doesn't make breaking changes to their maps library so I wouldn't be concerned. As far as integration efforts, it was hardly an effort. I was able to display test data over my existing application in about 20 minutes from first reading the deck.gl documentation. Due to the fact that Deck.gl operates independently of the Angular context, I can't image new Angular versions breaking compatibility with the exception of the build process.

I can clean up the demo and submit a PR today or Monday most likely.

@SterlingMcCall
Copy link
Author

@Pessimistress After further exploration in trying to make a clean demo, I discovered that it was not the custom webpack config with the modified mainFields property that resolved the deck.gl loading issue. It was because when I added the custom webpack config I also upgraded from Angular 7 to Angular 8 in the process.

Without modifying the default settings used by Angular 8, deck.gl works just fine.

@ibgreen I can still work on contributing an example Angular 8 app, or the still broken Angular 7 app I originally created so that this bug can potentially be fixed for compatibility with older versions of Angular, and possibly prevent the incompatibility from being re-introduced. However, upgrading to Angular 8 seems like the most simple solution for those able to perform the upgrade.

@ibgreen
Copy link
Collaborator

ibgreen commented Sep 17, 2019

@SterlingMcCall

Angular 8, deck.gl works just fine.

That is great news.

I can still work on contributing an example Angular 8 app, or the still broken Angular 7 app I originally created so that this bug can potentially be fixed for compatibility with older version.

Let's go with v8! Given that we are just starting to explore Angular support in deck.gl, I would focus on the latest, working version, i.e. v8. and mention in a README file that older versions have issues.

Later, if there is a huge uptake of angular users, and a lot of those users can't upgrade to Angular v8 for some reasons unrelated to deck.gl, we could always revisit this and try to create a working example for v7.

@SterlingMcCall
Copy link
Author

@ibgreen

Let's go with v8!

Okay. I noticed most of the apps test/apps/angular and examples/get-started/angular are very lightweight, often only a couple files. Using the angular CLI to generate a new Angular project results in multiple folders, stubbed tests, and almost two dozen files to conform to Angular best practices. Would the best demo be the absolute lightest weight working angular app? Or would it be the one more closely resembling what a user would see when using the CLI to make a new project?

@ibgreen
Copy link
Collaborator

ibgreen commented Sep 18, 2019

@SterlingMcCall

Good question. I'll let you make the call, but will say:

  • I am not quite sure what is best for the Angular community, but I would certainly have started with a minimal example.
  • The README could have information about any complications with the CLI.

In my mind, it is not really deck.gl's job to show Angular/React etc best project scaffolding practices, though if 99% of Angular users are using this scaffold and it is hard to get it working with deck.gl based on a minimal example, then it could make sense.

For instance, a lot of React users use create-react-app so it could make sense to have a get-started example for that, and the angular cli might be similar. But I would still personally prefer the minimal examples to really isolate a setup that works.

@lovanva
Copy link

lovanva commented Jan 13, 2020

@ibgreen

I'm getting a similar issue with our Angular app while pushing to our Dev and Test environments. The app loads the map and displays layers while serving app locally. But when we push code to dev and test i'm getting reference error and blank page. Looks like it's being called from luma.gl. We are on Angular v7. Is it the best solution to upgrade to v8? I do not see any of the Angular demo apps within the 'Test' or 'Examples' repo.

image

@lovanva
Copy link

lovanva commented Jan 14, 2020

The workaround for my issue is to switch aot and buildOptimizer to false in angular.json file.

@SterlingMcCall
Copy link
Author

@lovanva When I upgraded my app from angular 7 to angular 8 the issues I had with with AOT compilation and deck.gl were resolved. I can't imagine you would want to run your app in prod without the AOT or build optimizer enabled as a long term solution, so upgrading to 8 would be worth a try if it's an option.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

4 participants