Skip to content

fix: clarify @JsModule, @CssImport, @JavaScript, @StyleSheet semantics#24207

Draft
Artur- wants to merge 5 commits into
mainfrom
feature/clarify-javascript-jsmodule-stylesheet
Draft

fix: clarify @JsModule, @CssImport, @JavaScript, @StyleSheet semantics#24207
Artur- wants to merge 5 commits into
mainfrom
feature/clarify-javascript-jsmodule-stylesheet

Conversation

@Artur-

@Artur- Artur- commented Apr 27, 2026

Copy link
Copy Markdown
Member

Establishes a clean model:

  • @JsModule / @CssImport: build-time bundle sources, fed into Vite. App source: src/main/frontend/. Addon source: META-INF/frontend/ (with META-INF/resources/frontend/ kept for compatibility but deprecated).
  • @StyleSheet / @JavaScript: runtime <link>/<script> elements served by the servlet container from META-INF/resources/. Relative URLs always resolve against the context root, regardless of servlet mapping. @JavaScript has a type attribute (Type.SCRIPT default, or Type.MODULE for <script type="module">), so CDN-hosted and hand-authored ES modules can be loaded at runtime through annotations as well.

Incompatible user-facing changes

  1. @StyleSheet("foo.css") on a Component, app on custom servlet mapping (e.g. /app/*): now resolves to <contextroot>/foo.css instead of 404'ing under /app/foo.css. Bug fix; nothing to do. If you actually wanted servlet-mapping-relative loading, write @StyleSheet("base://foo.css").

Compatible changes

  1. @StyleSheet("../foo.css") (any .. path): now logs a WARN and is silently dropped at component level too (AppShell already did this). Remove the traversal — put the file at a non-traversing path under META-INF/resources/ and reference it directly.
  2. Multiple variants of the same @StyleSheet file ("foo.css" + "./foo.css" + "context://foo.css"): now deduplicated to one <link> tag.
  3. @JavaScript("./foo.js") or @JavaScript("foo.js") (bare relative, default type=Type.SCRIPT): build-time WARN "uses the deprecated bundled interpretation. Prepend context:// for a runtime <script> tag, set type=Type.MODULE for a runtime <script type="module"> tag, or migrate to @JsModule for bundling." Still bundles for now. Pick one:
    • @JsModule("./foo.js") if you wanted Vite to bundle it (file stays in src/main/frontend/ or addon META-INF/frontend/);
    • @JavaScript("context://foo.js") for a plain runtime <script> (move file to src/main/resources/META-INF/resources/foo.js or addon META-INF/resources/foo.js);
    • @JavaScript(value="foo.js", type=Type.MODULE) for a runtime <script type="module"> (same file location as the previous option).
  4. @JsModule("https://cdn.example.com/foo.js") (or any http://, https://, //, context://, base://, /abs): build-time WARN "is a runtime URL. @JsModule is for build-time bundle sources only; use @JavaScript(value="...", type=Type.MODULE) for a runtime <script type="module"> tag." Still loaded at runtime as <script type="module"> (no longer also bundled). Switch to @JavaScript(value="https://cdn.example.com/foo.js", type=Type.MODULE) to preserve the module semantics, or to plain @JavaScript("https://cdn.example.com/foo.js") if you actually wanted a classic <script> tag.
  5. New @JavaScript.type() attribute with values Type.SCRIPT (default, current behavior) and Type.MODULE (new, renders <script type="module">). Source-compatible: existing @JavaScript annotations keep their previous behavior.
  6. Add-on JAR with files under META-INF/resources/frontend/: once-per-jar build-time WARN "Addon '' contains frontend sources under META-INF/resources/frontend/. This location is deprecated; migrate them to META-INF/frontend/ (bundle sources for @JsModule/@CssImport) or to META-INF/resources/ (runtime resources for @StyleSheet/@JavaScript)." Still works. Split:
    • bundle-source files (referenced by @JsModule/@CssImport) → move to META-INF/frontend/.
    • runtime files (referenced by @StyleSheet/@JavaScript) → move to META-INF/resources/ (drop the frontend/ segment, adjust annotation values accordingly).

References

#22888, #23326, #16780 and the v25 web-component-path / CSS-loading forum threads.

@github-actions

github-actions Bot commented Apr 27, 2026

Copy link
Copy Markdown

Test Results

 1 451 files   - 1   1 451 suites   - 1   1h 22m 38s ⏱️ - 1m 20s
10 254 tests +2  10 186 ✅ +2  68 💤 ±0  0 ❌ ±0 
10 726 runs  +2  10 657 ✅ +2  69 💤 ±0  0 ❌ ±0 

Results for commit 83b5a00. ± Comparison against base commit 291ff06.

♻️ This comment has been updated with latest results.

@Artur- Artur- force-pushed the feature/clarify-javascript-jsmodule-stylesheet branch 2 times, most recently from 365f4b2 to 84ca840 Compare May 2, 2026 05:31
@sonarqubecloud

sonarqubecloud Bot commented May 2, 2026

Copy link
Copy Markdown

@mshabarov mshabarov moved this from 🔎Iteration reviews to ⚒️ In progress in Vaadin Flow | Hilla | Kits ongoing work May 13, 2026
@Artur- Artur- changed the title fix!: clarify @JsModule, @CssImport, @JavaScript, @StyleSheet semantics fix: clarify @JsModule, @CssImport, @JavaScript, @StyleSheet semantics May 28, 2026
@mshabarov mshabarov moved this from ⚒️ In progress to 🪵Product backlog in Vaadin Flow | Hilla | Kits ongoing work Jun 11, 2026
@Artur- Artur- force-pushed the feature/clarify-javascript-jsmodule-stylesheet branch 2 times, most recently from 62a3691 to 83166a7 Compare June 13, 2026 17:28
Artur- added 5 commits June 15, 2026 15:27
Lets a @javascript annotation render as a <script type="module"> tag
instead of a classic <script>, so hand-authored or CDN-hosted ES
modules can be loaded at runtime through annotations without going
through Vite. For build-time bundled ES modules @jsmodule remains the
right tool.

The new @JavaScript.Type enum has values SCRIPT (default, current
behavior) and MODULE. The annotation gains a type() attribute that
selects between them.

To make @javascript the unified entry point on the programmatic side
as well, this commit also:
- adds a new Page.addJavaScript(String url, LoadMode loadMode,
  JavaScript.Type type) overload that handles both classic <script>
  and <script type="module"> tags, with full LoadMode support for
  both;
- delegates the existing addJavaScript(String, LoadMode) and
  addJavaScript(String) overloads to the new method with
  Type.SCRIPT;
- deprecates Page.addJsModule(String) — recommend
  addJavaScript(url, loadMode, Type.MODULE) instead. The deprecated
  method keeps working for backwards compatibility.

UIInternals.addExternalDependencies routes both @javascript runtime
values and external @jsmodule values through the new addJavaScript
overload. @javascript values pass js.loadMode() and js.type()
straight through, so type=MODULE supports LAZY and INLINE load modes
just like type=SCRIPT.

FrontendClassVisitor.JSAnnotationVisitor reads the type enum via a
new visitEnum override and skips MODULE-typed values from the bundle
imports collection. The type attribute does not exist on @jsmodule,
so visitEnum is a no-op for it.

Existing @javascript usages keep their behavior: bare relative values
default to type=SCRIPT and continue to bundle (legacy interpretation),
external URLs continue to render as runtime <script> tags.
@javascript values with a runtime prefix (context://, base://, /abs,
http(s)://, //) are now served as runtime <script> elements rather than
bundled, matching how the corresponding @Stylesheet values are resolved.

- New FrontendDependencyUrlResolver.isRuntimeDependencyUrl recognises the
  runtime prefix table (http(s)://, //, context://, base://, /abs) and is
  the single source of truth for distinguishing runtime URLs from bundler
  import specifiers.
- UIInternals treats such @javascript values as runtime dependencies via
  isRuntimeJavaScript, alongside type=MODULE values.
- The scanner (FrontendClassVisitor) filters runtime-prefixed @javascript
  values out of the bundle imports.
- Bare-relative @javascript values still bundle for backwards
  compatibility but FrontendDependencies emits a one-time consolidated
  build-time warning per (class, value) recommending migration.
@jsmodule is for build-time bundle sources only. A @jsmodule value with a
runtime URL prefix (context://, base://, /abs, http(s)://, //) keeps
working at runtime via Page.addJsModule, but is now filtered out of the
bundle imports and triggers a build-time deprecation warning.

- The scanner (FrontendClassVisitor) tracks such values on
  ClassInfo.deprecatedRuntimeModules instead of adding them to the bundle
  imports.
- FrontendDependencies emits a one-time consolidated build-time warning
  per (class, value) recommending migration to
  @javascript(value=..., type=Type.MODULE) for runtime
  <script type="module"> loading.

This removes the previous double-load where an external @jsmodule URL was
both bundled and loaded at runtime.
…layout

TaskCopyFrontendFiles still scans META-INF/resources/frontend/ for
backwards compatibility, but that location is deprecated. Emit a
once-per-jar/dir warning recommending the split layout: bundle sources
for @JsModule/@CssImport under META-INF/frontend/, and runtime resources
for @StyleSheet/@javascript under META-INF/resources/ (served directly by
the servlet container).
Rewrites the Javadoc for all four dependency annotations to describe the
bundle-source vs runtime distinction, the source locations, and the URL
prefix table. Page.addStyleSheet/addJavaScript Javadoc cross-references
the annotation prefix tables.

Establishes a clean four-quadrant model:
- @jsmodule / @CssImport: build-time bundle sources, fed into Vite. App
  source: src/main/frontend/. Addon source: META-INF/frontend/ (with
  META-INF/resources/frontend/ kept for compatibility but deprecated).
- @Stylesheet / @javascript: runtime <link>/<script> elements served by
  the servlet container from META-INF/resources/. Relative URLs always
  resolve against the context root, regardless of servlet mapping.

References #22888, #23326, #16780 and
the v25 web-component-path / CSS-loading forum threads.
@Artur- Artur- force-pushed the feature/clarify-javascript-jsmodule-stylesheet branch from 83166a7 to 83b5a00 Compare June 16, 2026 11:00
@sonarqubecloud

Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
0.0% Coverage on New Code (required ≥ 80%)

See analysis details on SonarQube Cloud

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

Status: 🪵Product backlog

Development

Successfully merging this pull request may close these issues.

2 participants