Skip to content

Commit 4d1ecd9

Browse files
authored
fix: CssImport in webcomponentexporter should not show in document (#22694)
* fix: CssImport in webcomponentexporter should not show in document Any CssImport added to a WebComponentExporter should only be applied to the WebComponent and not apply to elements outside the component. Fixes #22637 * fix css offset
1 parent b3affb2 commit 4d1ecd9

File tree

16 files changed

+511
-9
lines changed

16 files changed

+511
-9
lines changed

flow-server/src/main/java/com/vaadin/flow/server/frontend/AbstractUpdateImports.java

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -263,13 +263,14 @@ private void adaptCssInjectForWebComponent(ListIterator<String> iterator,
263263

264264
private List<String> mergeWebComponentOutputLines(
265265
Map<File, List<String>> outputFiles) {
266-
return Stream.concat(
267-
outputFiles
268-
.getOrDefault(appShellImports, Collections.emptyList())
269-
.stream(),
270-
outputFiles.getOrDefault(generatedFlowImports,
271-
Collections.emptyList()).stream())
272-
.distinct().toList();
266+
List<String> merged = new ArrayList<>();
267+
merged.addAll(outputFiles.getOrDefault(appShellImports,
268+
Collections.emptyList()));
269+
merged.addAll(outputFiles.getOrDefault(generatedFlowWebComponentImports,
270+
Collections.emptyList()));
271+
merged.addAll(outputFiles.getOrDefault(generatedFlowImports,
272+
Collections.emptyList()));
273+
return merged.stream().distinct().toList();
273274
}
274275

275276
private void writeWebComponentImports(List<String> lines) {
@@ -308,6 +309,7 @@ private Map<File, List<String>> process(Map<ChunkInfo, List<CssData>> css,
308309
Map<ChunkInfo, List<String>> lazyCss = new LinkedHashMap<>();
309310
List<CssData> eagerCssData = new ArrayList<>();
310311
List<CssData> appShellCssData = new ArrayList<>();
312+
List<CssData> webComponentCssData = new ArrayList<>();
311313
if (FrontendUtils.isTailwindCssEnabled(options)) {
312314
appShellCssData.add(new CssData(TAILWIND_IMPORT, null, null, null));
313315
}
@@ -332,6 +334,8 @@ private Map<File, List<String>> process(Map<ChunkInfo, List<CssData>> css,
332334
} else {
333335
if (entry.getKey().equals(ChunkInfo.APP_SHELL)) {
334336
appShellCssData.addAll(entry.getValue());
337+
} else if (entry.getKey().equals(ChunkInfo.WEB_COMPONENT)) {
338+
webComponentCssData.addAll(entry.getValue());
335339
} else {
336340
eagerCssData.addAll(entry.getValue());
337341
}
@@ -429,6 +433,11 @@ private Map<File, List<String>> process(Map<ChunkInfo, List<CssData>> css,
429433
}
430434
mainLines.addAll(getModuleLines(eagerJavascript));
431435

436+
if (!webComponentCssData.isEmpty()) {
437+
files.put(generatedFlowWebComponentImports,
438+
getCssLines(webComponentCssData, cssLineOffset));
439+
}
440+
432441
// Move all imports to the top
433442
List<String> copy = new ArrayList<>(mainLines);
434443
copy.removeIf(line -> !line.startsWith("import "));

flow-server/src/main/java/com/vaadin/flow/server/frontend/scanner/ChunkInfo.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@ public class ChunkInfo {
4343
public static final ChunkInfo GLOBAL = new ChunkInfo(
4444
EntryPointType.INTERNAL, null, null, false);
4545

46+
public static final ChunkInfo WEB_COMPONENT = new ChunkInfo(
47+
EntryPointType.WEB_COMPONENT, "webComponent", null, false);
48+
4649
private final EntryPointType type;
4750
private final String name;
4851

flow-server/src/main/java/com/vaadin/flow/server/frontend/scanner/FrontendDependencies.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -326,6 +326,9 @@ private ChunkInfo getChunkInfo(EntryPointData data) {
326326
if (data.getType() == EntryPointType.APP_SHELL) {
327327
return ChunkInfo.APP_SHELL;
328328
}
329+
if (data.getType() == EntryPointType.WEB_COMPONENT) {
330+
return ChunkInfo.WEB_COMPONENT;
331+
}
329332
if (data.getType() == EntryPointType.INTERNAL) {
330333
return ChunkInfo.GLOBAL;
331334
}

flow-server/src/main/java/com/vaadin/flow/server/frontend/scanner/FullDependenciesScanner.java

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
import org.slf4j.LoggerFactory;
3434

3535
import com.vaadin.experimental.FeatureFlags;
36+
import com.vaadin.flow.component.WebComponentExporter;
3637
import com.vaadin.flow.component.dependency.CssImport;
3738
import com.vaadin.flow.component.dependency.JavaScript;
3839
import com.vaadin.flow.component.dependency.JsModule;
@@ -75,6 +76,7 @@ class FullDependenciesScanner extends AbstractDependenciesScanner {
7576
private HashMap<String, List<String>> devAssets = new HashMap<>();
7677
private List<CssData> themeCssData;
7778
private List<CssData> cssData;
79+
private List<CssData> webComponentCssData;
7880
private List<String> scripts;
7981
private List<String> scriptsDevelopment;
8082
private List<String> modules;
@@ -154,6 +156,7 @@ class FullDependenciesScanner extends AbstractDependenciesScanner {
154156

155157
themeCssData = new ArrayList<>();
156158
cssData = new ArrayList<>();
159+
webComponentCssData = new ArrayList<>();
157160
discoverCss();
158161

159162
if (!reactEnabled) {
@@ -220,7 +223,8 @@ public Map<ChunkInfo, List<String>> getScriptsDevelopment() {
220223
public Map<ChunkInfo, List<CssData>> getCss() {
221224
// Map theme CSS to the APP_SHELL chunk
222225
return Map.ofEntries(Map.entry(ChunkInfo.APP_SHELL, themeCssData),
223-
Map.entry(ChunkInfo.GLOBAL, cssData));
226+
Map.entry(ChunkInfo.GLOBAL, cssData),
227+
Map.entry(ChunkInfo.WEB_COMPONENT, webComponentCssData));
224228
}
225229

226230
@Override
@@ -320,10 +324,14 @@ private void discoverCss() {
320324
.getAnnotatedClasses(loadedAnnotation);
321325
var themeCss = new LinkedHashSet<CssData>();
322326
var globalCss = new LinkedHashSet<CssData>();
327+
var webComponentCss = new LinkedHashSet<CssData>();
328+
323329
for (Class<?> clazz : annotatedClasses) {
324330
classes.add(clazz.getName());
325331
var isAppShellClass = AppShellConfigurator.class
326332
.isAssignableFrom(clazz);
333+
var isWebcomponentExporter = WebComponentExporter.class
334+
.isAssignableFrom(clazz);
327335
var isThemeClass = AbstractTheme.class.isAssignableFrom(clazz);
328336
if (isThemeClass && (themeDefinition == null
329337
|| !clazz.equals(themeDefinition.getTheme()))) {
@@ -336,10 +344,13 @@ private void discoverCss() {
336344
imports.stream()
337345
.forEach(imp -> ((isAppShellClass || isThemeClass)
338346
? themeCss
339-
: globalCss).add(createCssData(imp)));
347+
: isWebcomponentExporter ? webComponentCss
348+
: globalCss)
349+
.add(createCssData(imp)));
340350
}
341351
themeCssData.addAll(themeCss);
342352
cssData.addAll(globalCss);
353+
webComponentCssData.addAll(webComponentCss);
343354
} catch (ClassNotFoundException exception) {
344355
throw new IllegalStateException(
345356
COULD_NOT_LOAD_ERROR_MSG + CssData.class.getName(),

flow-tests/test-embedding/README.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,3 +39,31 @@ be implemented. Its method `assertThatThemedComponentWasChosen` is used to
3939
validate that the embedded component being tested has the correct theme
4040
applied. See modules `test-embedding-generic` and
4141
`test-embedding-generic-compatibility` for examples.
42+
43+
### Note on static webapp html file usage
44+
45+
- DO NOT create a file named web-component.html in src/main/webapp/
46+
- The file web-component.html is reserved and auto-generated in src/main/frontend/web-component.html by the Vaadin build
47+
process
48+
- If you create a manual web-component.html in the webapp folder, it will be served directly by the servlet container,
49+
bypassing Vite's transformation pipeline
50+
- This prevents the automatic injection of vaadin-web-component.ts, which initializes the Flow client and sets up
51+
required APIs like window.Vaadin.Flow.registerWidgetset
52+
- Result: Your embedded web components will fail to connect to the server with errors like
53+
$wnd.Vaadin.Flow.registerWidgetset is not a function
54+
55+
Correct approach:
56+
- Use index.html or any other name for your main HTML files in src/main/webapp/
57+
- Let Vaadin generate src/main/frontend/web-component.html automatically during the build
58+
- Access your embedded web components through the URLs served by the Vaadin servlet, which will properly transform the
59+
auto-generated web-component.html
60+
61+
File structure:
62+
src/main/
63+
├── frontend/
64+
│ └── web-component.html ← Auto-generated, includes proper initialization
65+
├── webapp/
66+
│ └── index.html ← Your custom HTML (use any name except web-component.html)
67+
└── java/
68+
└── com/example/
69+
└── MyComponentExporter.java

flow-tests/test-embedding/pom.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,8 @@
5555
<!-- Custom theme tests -->
5656
<module>test-embedding-application-theme</module>
5757
<module>test-embedding-reusable-theme</module>
58+
<module>test-embedding-style-containment</module>
59+
<module>test-embedding-style-containment/pom-production.xml</module>
5860
</modules>
5961
</profile>
6062
</profiles>
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!--
3+
~ Copyright 2000-2025 Vaadin Ltd.
4+
~
5+
~ Licensed under the Apache License, Version 2.0 (the "License"); you may not
6+
~ use this file except in compliance with the License. You may obtain a copy of
7+
~ the License at
8+
~
9+
~ http://www.apache.org/licenses/LICENSE-2.0
10+
~
11+
~ Unless required by applicable law or agreed to in writing, software
12+
~ distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13+
~ WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14+
~ License for the specific language governing permissions and limitations under
15+
~ the License.
16+
-->
17+
18+
<project xmlns="http://maven.apache.org/POM/4.0.0"
19+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
20+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
21+
<modelVersion>4.0.0</modelVersion>
22+
<parent>
23+
<groupId>com.vaadin</groupId>
24+
<artifactId>test-embedding</artifactId>
25+
<version>25.0-SNAPSHOT</version>
26+
</parent>
27+
28+
<artifactId>test-embedding-style-containment-production</artifactId>
29+
<packaging>war</packaging>
30+
<name>Flow Embedding style containment test production</name>
31+
32+
<properties>
33+
<maven.compiler.source>24</maven.compiler.source>
34+
<maven.compiler.target>24</maven.compiler.target>
35+
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
36+
<maven.deploy.skip>true</maven.deploy.skip>
37+
</properties>
38+
39+
<dependencies>
40+
<dependency>
41+
<groupId>com.vaadin</groupId>
42+
<artifactId>flow-html-components-testbench</artifactId>
43+
<version>${project.version}</version>
44+
<scope>test</scope>
45+
</dependency>
46+
</dependencies>
47+
48+
<build>
49+
<plugins>
50+
<!-- This module is mapped to default web context -->
51+
<plugin>
52+
<groupId>org.eclipse.jetty.ee10</groupId>
53+
<artifactId>jetty-ee10-maven-plugin</artifactId>
54+
</plugin>
55+
56+
<plugin>
57+
<groupId>com.vaadin</groupId>
58+
<artifactId>flow-maven-plugin</artifactId>
59+
<executions>
60+
<execution>
61+
<goals>
62+
<goal>prepare-frontend</goal>
63+
<goal>build-frontend</goal>
64+
</goals>
65+
</execution>
66+
</executions>
67+
</plugin>
68+
</plugins>
69+
</build>
70+
71+
</project>
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!--
3+
~ Copyright 2000-2025 Vaadin Ltd.
4+
~
5+
~ Licensed under the Apache License, Version 2.0 (the "License"); you may not
6+
~ use this file except in compliance with the License. You may obtain a copy of
7+
~ the License at
8+
~
9+
~ http://www.apache.org/licenses/LICENSE-2.0
10+
~
11+
~ Unless required by applicable law or agreed to in writing, software
12+
~ distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13+
~ WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14+
~ License for the specific language governing permissions and limitations under
15+
~ the License.
16+
-->
17+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
18+
<modelVersion>4.0.0</modelVersion>
19+
<parent>
20+
<groupId>com.vaadin</groupId>
21+
<artifactId>test-embedding</artifactId>
22+
<version>25.0-SNAPSHOT</version>
23+
</parent>
24+
25+
<artifactId>test-embedding-style-containment</artifactId>
26+
<packaging>war</packaging>
27+
<name>Flow Embedding style containment test</name>
28+
29+
<properties>
30+
<maven.compiler.source>24</maven.compiler.source>
31+
<maven.compiler.target>24</maven.compiler.target>
32+
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
33+
<maven.deploy.skip>true</maven.deploy.skip>
34+
</properties>
35+
36+
<dependencies>
37+
<dependency>
38+
<groupId>com.vaadin</groupId>
39+
<artifactId>vaadin-dev-server</artifactId>
40+
<version>${project.version}</version>
41+
</dependency>
42+
43+
<dependency>
44+
<groupId>com.vaadin</groupId>
45+
<artifactId>flow-html-components-testbench</artifactId>
46+
<version>${project.version}</version>
47+
<scope>test</scope>
48+
</dependency>
49+
</dependencies>
50+
<build>
51+
<plugins>
52+
<!-- This module is mapped to default web context -->
53+
<plugin>
54+
<groupId>org.eclipse.jetty.ee10</groupId>
55+
<artifactId>jetty-ee10-maven-plugin</artifactId>
56+
</plugin>
57+
58+
<plugin>
59+
<groupId>com.vaadin</groupId>
60+
<artifactId>flow-maven-plugin</artifactId>
61+
<executions>
62+
<execution>
63+
<goals>
64+
<goal>prepare-frontend</goal>
65+
</goals>
66+
</execution>
67+
</executions>
68+
</plugin>
69+
</plugins>
70+
</build>
71+
72+
</project>
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
.userName {
2+
background-color: blue;
3+
}
4+
5+
.password {
6+
background-color: red;
7+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<!DOCTYPE html>
2+
<!--
3+
This file is auto-generated by Vaadin.
4+
-->
5+
6+
<html>
7+
<head>
8+
<!-- vaadin-web-component.ts is included here automatically (either by the dev server or during the build) -->
9+
</head>
10+
</html>

0 commit comments

Comments
 (0)