fix!: clarify @JsModule, @CssImport, @JavaScript, @StyleSheet semantics#24207
Draft
fix!: clarify @JsModule, @CssImport, @JavaScript, @StyleSheet semantics#24207
Conversation
AppShellRegistry.resolveStyleSheetHref expanded context://-prefixed @Stylesheet values server-side using request.getContextPath() + "/", producing absolute server paths like <link href="/foo/styles.css"> that get baked into index.html. This breaks behind reverse proxies that don't preserve the servlet container's context path in the public URL: the server emits /foo/styles.css but the browser fetches it from the public host where /foo/ doesn't exist. Use service.getContextRootRelativePath(request) instead — the same servlet-relative path (./, ../, etc.) that the bootstrap callback populates into CONTEXT_ROOT_URL for the UIDL path. The resulting href is resolved by the browser against <base>, which Vaadin sets from the actual request URL (honoring X-Forwarded-* headers). This brings AppShell-level @Stylesheet resolution in line with the component-level UIDL path, which already used the relative form via the client-side URIResolver. Test fixtures updated to reflect the new servlet-relative hrefs. AppShellRegistryAuraAutoLoadTest had a Mockito mock that returned null for getContextRootRelativePath; it now stubs "./". Related to #24218.
@Stylesheet currently produces broken <link> elements when the Vaadin servlet is mapped at a non-root path (vaadin.urlMapping=/ui/* etc.): the browser resolves the bare relative href against <base> (which points to the servlet mapping path), so a file at META-INF/resources/styles.css is fetched as /ui/styles.css and 404s. Users had to know to write @Stylesheet("context://styles.css") explicitly to step out of the servlet path. Frame the right behavior into the framework instead: - New FrontendDependencyUrlResolver.resolveToContextRoot extracts the prefix-handling rules into one place: http(s)://, //, context://, base:// pass through unchanged; "/" is treated as an absolute server path; "./" leads strip to a context-root-relative value; everything else gets a context:// prefix prepended. Path traversals are rejected. - UIInternals.addComponentDependencies normalizes @Stylesheet values through the resolver before storing them on the dependency list, so component-level @Stylesheet now renders correctly under any servlet mapping. The same normalization keys ActiveStyleSheetTracker so spelling variants of the same file (foo.css, ./foo.css, context://foo.css) deduplicate to a single <link>. - AppShellRegistry.resolveStyleSheetHref delegates to the same resolver, replacing the inline rule set. The trailing BootstrapUriResolver call continues to expand context:// using the servlet-relative path produced by getContextRootRelativePath, so AppShell-level resolution stays consistent with the UIDL path. Test fixtures updated for the canonical context://-prefixed URLs in the dependency list. UidlWriterTest also registers inline test resources at the leading-slash path (/inline.css) to match the servlet container lookup that resolveResource produces for a context:// value. Fixes #22888.
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 Type enum has values SCRIPT (default, current behavior) and MODULE. With type=MODULE: - UIInternals.addExternalDependencies dispatches the value to Page.addJsModule(resolved) instead of Page.addJavaScript. The value is normalized via FrontendDependencyUrlResolver.resolveToContextRoot, so bare relatives become context://<value> and are served as static resources by the servlet container under any servlet mapping. - 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. LoadMode is ignored for type=MODULE because Page.addJsModule does not take a LoadMode parameter; documented on @JavaScript.loadMode.
bb8ba7c to
365f4b2
Compare
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. Concrete changes: - New FrontendDependencyUrlResolver with resolveToContextRoot and isRuntimeDependencyUrl. Single source of truth for the prefix table: http(s)://, //, context://, base://, /abs are runtime; bare relatives and ./foo are normalized to context://foo. - AppShellRegistry.resolveStyleSheetHref delegates to the resolver. - UIInternals.addComponentDependencies eagerly resolves @Stylesheet values via the same resolver, so component-level @Stylesheet now bypasses the Vaadin servlet mapping the same way AppShell-level @Stylesheet already did. Fixes the issue where @Stylesheet("foo.css") returned 404 when the Vaadin servlet was mapped at a non-root path. - @javascript values with a runtime prefix (context://, base://, /abs, http(s)://, //) are now served as runtime <script> elements rather than bundled. Bare-relative @javascript values still bundle for backwards compatibility but emit a one-time consolidated build-time warning per (class, value). - @jsmodule with a runtime URL is similarly deprecated. The scanner filters such values out of the bundle imports and tracks them on ClassInfo.deprecatedRuntimeModules. FrontendDependencies emits a consolidated build-time warning recommending migration to @javascript(value=..., type=Type.MODULE) for runtime <script type="module"> loading. The runtime path in UIInternals.addExternalDependencies continues to load external @jsmodule URLs as runtime modules so existing apps keep working; the previous double-load (bundle + runtime) is gone. - TaskCopyFrontendFiles emits a once-per-jar/dir warning when an addon uses META-INF/resources/frontend/, recommending the split layout. - Javadoc rewrites for all four annotations describe the bundle-source vs runtime distinction, source locations, and the URL prefix table. - Page.addStyleSheet/addJavaScript Javadoc cross-references the annotation prefix tables. References #22888, #23326, #16780 and the v25 web-component-path / CSS-loading forum threads.
365f4b2 to
84ca840
Compare
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



Establishes a clean model:
@JsModule/@CssImport: build-time bundle sources, fed into Vite. App source:src/main/frontend/. Addon source:META-INF/frontend/(withMETA-INF/resources/frontend/kept for compatibility but deprecated).@StyleSheet/@JavaScript: runtime<link>/<script>elements served by the servlet container fromMETA-INF/resources/. Relative URLs always resolve against the context root, regardless of servlet mapping.@JavaScripthas atypeattribute (Type.SCRIPTdefault, orType.MODULEfor<script type="module">), so CDN-hosted and hand-authored ES modules can be loaded at runtime through annotations as well.Incompatible user-facing changes
@StyleSheet("foo.css")on a Component, app on custom servlet mapping (e.g./app/*): now resolves to<contextroot>/foo.cssinstead 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
@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 underMETA-INF/resources/and reference it directly.@StyleSheetfile ("foo.css"+"./foo.css"+"context://foo.css"): now deduplicated to one<link>tag.@JavaScript("./foo.js")or@JavaScript("foo.js")(bare relative, defaulttype=Type.SCRIPT): build-time WARN "uses the deprecated bundled interpretation. Prependcontext://for a runtime<script>tag, settype=Type.MODULEfor a runtime<script type="module">tag, or migrate to@JsModulefor bundling." Still bundles for now. Pick one:@JsModule("./foo.js")if you wanted Vite to bundle it (file stays insrc/main/frontend/or addonMETA-INF/frontend/);@JavaScript("context://foo.js")for a plain runtime<script>(move file tosrc/main/resources/META-INF/resources/foo.jsor addonMETA-INF/resources/foo.js);@JavaScript(value="foo.js", type=Type.MODULE)for a runtime<script type="module">(same file location as the previous option).@JsModule("https://cdn.example.com/foo.js")(or anyhttp://,https://,//,context://,base://,/abs): build-time WARN "is a runtime URL.@JsModuleis 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.@JavaScript.type()attribute with valuesType.SCRIPT(default, current behavior) andType.MODULE(new, renders<script type="module">). Source-compatible: existing@JavaScriptannotations keep their previous behavior.META-INF/resources/frontend/: once-per-jar build-time WARN "Addon '' contains frontend sources underMETA-INF/resources/frontend/. This location is deprecated; migrate them toMETA-INF/frontend/(bundle sources for@JsModule/@CssImport) or toMETA-INF/resources/(runtime resources for@StyleSheet/@JavaScript)." Still works. Split:@JsModule/@CssImport) → move toMETA-INF/frontend/.@StyleSheet/@JavaScript) → move toMETA-INF/resources/(drop thefrontend/segment, adjust annotation values accordingly).References
#22888, #23326, #16780 and the v25 web-component-path / CSS-loading forum threads.