Production build messes up javascript files in bundle

Hello, everyone.

After some research about the build process of a Vaadin app, I come to this forum with no luck so far to solve the situation I’m in. I was trying to generate a production build of a spring app, which unfortunately I can’t get to work properly.

The problem goes like this:

  • I have a vaadin-spring project, from which I am trying to build a WAR to deploy on a Tomcat server. (At first, I thought the problem was here, because by default, the project starter is configured to build a JAR - I set up additional configuration to build a WAR - But this is not the case, as I describe below).
  • When I run the project in development mode, the Javascript imports appear to be all good and sorted in the same way as my annotations.
  • But, when I build the project for production with mvn clean package -P productionMode, the scripts are now bundled and with different order to which I described in my MainView (I tried disabling transpilation and minification, but results were no different).
  • This leads to some code that depends on another, to be loaded first, without its dependencies being loaded yet.
  • So, when the users access the server, the Javascript plugins are not shown.

For example, I want these libraries in my project (and in this same order):

But, the bundle loads first JSMaps, then ChartsJS and finally jQuery, breaking JSMaps which depends on jQuery.
I know a solution could be to place all of the JS inside a single JS file, but I don’t think that’s the way to go.

I’m pretty sure the problem is not because of additional configuration in my project, as I was able to replicate the behavior in a fresh starter template for vaadin-spring.

The order in which annotations describe the Javascript to be loaded is as follows

@JavaScript("js/charts.js")
@JavaScript("js/jquery.js")
@JavaScript("js/jsmaps/jsmaps-libs.js")
@JavaScript("js/jsmaps/jsmaps.js")
@JavaScript("js/maps/mexico.js")

But the bundled versions for ES6 and ES5 load the files in ALMOST a reverse order.

mexico.js
jsmaps.js
jquery.js
jsmaps-libs.js
charts.js

Maybe I’m missing something trivial, but I’d rather learn from this instead of doing a dirty solution.
I’ve attached the project on which I was able to replicate the behavior (it must be imported as an Existing Maven Project in Eclipse).

Steps:

  • When I run the project from Eclipse I can see the libraries loaded correctly in the browser.
  • But, after I execute mvn clean package -P productionMode from the command line, inside the generated JAR, the bundles can be accessed just to confirm that the dependencies are being loaded in a weird way. You should try opening them to see the logs I added to the console, as they show the order on which the libraries are being loaded.

Thanks to everyone. I appreciate any comment or suggestion.
17461261.rar (264 KB)

Hi, thanks for the detailed explanation. I think you have to use /frontend/... in the URL:

@JavaScript("/frontend/js/charts.js")
@JavaScript("/frontend/js/jquery.js")

@JavaScript("/frontend/js/jsmaps/jsmaps-libs.js")
@JavaScript("/frontend/js/jsmaps/jsmaps.js")
@JavaScript("/frontend/js/maps/mexico.js")

You can also try with:

private void addDependencies() {
	UI.getCurrent().getPage().addJavaScript("/frontend/js/charts.js");
	UI.getCurrent().getPage().addJavaScript("/frontend/js/jquery.js");
	UI.getCurrent().getPage().addJavaScript("/frontend/js/jsmaps/jsmaps-libs.js");
	UI.getCurrent().getPage().addJavaScript("/frontend/js/jsmaps/jsmaps.js");
	UI.getCurrent().getPage().addJavaScript("/frontend/js/maps/mexico.js");
}

Please let us know if that works :slight_smile:

Thanks for your help.

I had a misconception about the @Javascript tag, as it is a Component level way of adding javascipt files (or am I still wrong?).

These lines did the trick:

private void addDependencies() {
	UI.getCurrent().getPage().addJavaScript("/frontend/js/charts.js");
	UI.getCurrent().getPage().addJavaScript("/frontend/js/jquery.js");
	UI.getCurrent().getPage().addJavaScript("/frontend/js/jsmaps/jsmaps-libs.js");
	UI.getCurrent().getPage().addJavaScript("/frontend/js/jsmaps/jsmaps.js");
	UI.getCurrent().getPage().addJavaScript("/frontend/js/maps/mexico.js");
}

Thank you so much :slight_smile:

Good to hear you solved the issue. However, didn’t adding /frontend/... to the URL in the @JavaScript annotation work? Actually, you should use frontend://... (for example @JavaScript("frontend://js/charts.js")) which is internally resolved to the frontend directory by Flow.

Alejandro Duarte:
Good to hear you solved the issue. However, didn’t adding /frontend/... to the URL in the @JavaScript annotation work? Actually, you should use frontend://... (for example @JavaScript("frontend://js/charts.js")) which is internally resolved to the frontend directory by Flow.

At first, I tried adding /frontend/... to all of my @JavaScript annotations, but the context path of the application got ignored completely. So I had to add it manually ("/MyContextPath/frontend/..."), and I don’t think that’s how it should be done. But even though in development mode the scripts were added correctly, when I tried to build the application in production mode, the bundle still had the scripts mixed, with some dependendants being loaded before its dependencies.

Then, I tried using UI.getCurrent().getPage().addJavaScript("..."), and it finally got the scripts working right on both modes: production and development (but with the same "/MyContextPath/frontend/..." string at the beginning of all my javascript imports). A couple of things I noticed is that the bundle is no longer used, and when the scripts are added this way, if /frontend/ or frontend:// are ignored in the string, Flow atomatically tries sending something like "./frontend-es6/js/myFile.js", but that folder (or the es5 version) are not present in my application, or at least not loaded in the server, because they do exist in the WAR file.

I just tried the "frontend://..." string inside my @JavaScript annotations, but the javascript files bundled in production are not in the correct order :frowning: It’s the same situation. Some dependants are loaded before its dependencies. The correct order is the one I specify with the annotations, and the docs say the scripts are guaranteed to be loaded in that order.

I think I’ll stick with the UI.getCurrent().getPage().addJavaScript("...") form for now, until I understand what’s really happening. Thanks for all your help :slight_smile: I recommend you check the file attached to the first post to understand what’s happening. Try running Application.java from Eclipse, and check how the scripts are loaded in the browser. Then, generate the productionMode build with Maven. Once created, just open the JAR, or run it, and check the bundle file. I added some console.log() just to make sure how and in what order the scripts are being loaded.

Thanks once again. I hope this helps :slight_smile: Greetings from Mexico.

How about loading the js files in an html file. For example, main-view-scripts.html with:

<script src="jquery.js"></script>
<script src="charts.js"></script>
<script src="jsmaps/jsmaps-libs.js"></script>
<script src="jsmaps/jsmaps.js"></script>
<script src="maps/mexico.js"></script>
@Route
@HtmlImport("frontend://js/main-view-scripts.html")
public class MainView extends VerticalLayout {
    ...
}