flow: JavaScript annotation doesn't work with context:// URLs in npm mode

I have the @JavaScript("context://js/test.js") attached to my route, in hopes to load JavaScript files as plain JS (not as a module) from src/main/webapp/js/test.js.

However, Vaadin tries to look up the javascript file from the node_modules and fails.

Minimal reproducible example

  1. The Skeleton Starter project: Add @JavaScript("context://js/test.js") annotation to the MainView class.
  2. Run the Skeleton Starter.

Expected behavior

The produced HTML page should load a script from src/main/webapp/js/test.js.

Actual behavior

The webpack compilation fails with

 Failed to find the following imports in the `node_modules` tree:
      - context://js/test.js
  If the build fails, check that npm packages are installed.

Versions:

- Vaadin / Flow version: 4.1.27 / 2.1.9
- Java version: 8
- OS version:
- Browser version (if applicable):
- Application Server (if applicable):
- IDE (if applicable): Intellij

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Comments: 26 (23 by maintainers)

Commits related to this issue

Most upvoted comments

@AB-xdev I’m very sorry, you’re right. I think the commits were pushed to our cherry-pick branch for 2.3, but someone accidentally deleted it and created new one for picking changes into without this change. This is our mistake, we’ll do a new 2.3.7 release ASAP and push the changes there. Sorry for the mess.

@AB-xdev Hi thanks for the heads up - we simply missed picking this. I’ve pushed it to the cherry-pick branch and it should then be in 2.3.5 and another 14.3 release in two weeks or so.

The idea is that the server-side URI resolver will translate context://foo.js into foo.js (or maybe the equivalent ./foo.js) which is included as src for the script tag. This means that the file needs to be put in a location from where static files are served by the servlet container, e.g. src/main/webapp/foo.js if deploying as a .war, rather than included in the frontend/ directory.

@knoobie , your suggestion is far away from the original issue. It may be a separate ticket because of location and order. These add a new feature which is completely absent at the moment. So I would suggest do not mix these things here.

type looks reasonable. But then we have a problem (as always) with compatibility:

  • if type is NPM then value should be used as a location of a file to bundle it
  • and we currently have the exception for “external URLs” : such URL is considered as a plain JS and won’t be included into a bundle but will be added as a plain script tag.

We have to keep backward compatibility and this limits us in the decisions .

I would really go with type to avoid any confusion with JavaScript but then I don’t understand what to do with the special case.

When the developer adds JavaScript - he should be able to decide what Vaadin does with the file. He knows the best how the code has to be inserted, interpreted and executed. As stated above, some files aren’t in npm.

Proposal: Change JavaScript or JsModule to something like this:

public @interface JavaScript {

String value();

Type type default Type.NPM; // Enum of „NPM, EXTERNAL_RESOURCE, INTERNAL_RESOURCE...“ 

Location location default Location.BUNDLE; // Enum of „HEAD, BODY, BUNDLE...“

Integer order() default -1; // Order in which the file should be added to head or body. Used if files depend on each other and have to be inserted in the correct order to work 
}



This isn’t really perfect - just a rough idea to work with. This could help with the current situation, because flow doesn’t have to „guess“ what the user wants - the user says it. Totally transparent without magic.

context:// refers to a file deployed to the servlet context path. I’m not 100% sure what an “internal” URL is but it certainly does not sound like one. Why would we want to ignore it instead of making it work?

@denis-anisimov I’d expect context://-based URLs to still work even in npm mode. Imagine that you have a bunch of old javascript files that you need to keep around for compatibility reasons (e.g. https://www.tiny.cloud/ which is not distributed via npm and doesn’t support webcomponents); the most obvious way is to place those in src/main/webapp/js and load them via @JavaScript("context://js/test.js"), especially since Page.addJavaScript("context://js/test.js") works also in npm mode.

Documenting that @JavaScript doesn’t support context:// (but leaving Page.addJavaScript() as-is which does support context:// and is perceived as an imperative alias for @JavaScript) would create confusion amongst the programmers. Therefore I insist on fixing this bug.

I don’t intuitively perceive @JavaScript is an alias for @JsModule since @JavaScript references plain global javascript files while @JsModule uses the new ES6 module mumbo-jumbo whatever.