Skip to content

Commit ce705bb

Browse files
authored
fix: Copy assets should keep subfolders (#22717)
The asset files should end up in subfolders for recursive matches. Fixes #22713
1 parent 067772f commit ce705bb

File tree

2 files changed

+62
-3
lines changed

2 files changed

+62
-3
lines changed

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

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -127,9 +127,10 @@ private void handleAsset(String npmModule, String npmAsset) {
127127
File npmModuleDir = new File(options.getNodeModulesFolder(), npmModule);
128128

129129
List<Path> paths = collectFiles(npmModuleDir.toPath(), rule.copyRule);
130+
Path basePath = getBasePath(npmModuleDir.toPath(), rule.copyRule);
130131

131132
paths.stream().map(Path::toFile).forEach(file -> {
132-
copyFileToTarget(file, new File(staticOutput, rule.targetFolder));
133+
copyFileToTarget(file, rule, basePath);
133134
});
134135
}
135136

@@ -144,8 +145,13 @@ private Rule getRule(String npmAsset, String npmModule) {
144145
return rule;
145146
}
146147

147-
private void copyFileToTarget(File file, File targetFolder) {
148-
File destFile = new File(targetFolder, file.getName());
148+
private void copyFileToTarget(File file, Rule copyRule, Path basePath) {
149+
File baseDestinationFolder = new File(staticOutput,
150+
copyRule.targetFolder);
151+
152+
Path relativePath = basePath.relativize(file.toPath());
153+
File destFile = new File(baseDestinationFolder,
154+
relativePath.toString());
149155
// Copy file to a target path, if target file doesn't exist
150156
// or if file to copy is newer.
151157
if (!destFile.exists()
@@ -162,6 +168,36 @@ private void copyFileToTarget(File file, File targetFolder) {
162168
}
163169
}
164170

171+
private Path getBasePath(Path npmModuleDir, String copyRule) {
172+
// Extract the static part of the copy rule (before any wildcards)
173+
String basePathStr = copyRule;
174+
175+
// Remove leading slashes
176+
if (basePathStr.startsWith("/")) {
177+
basePathStr = basePathStr.substring(1);
178+
}
179+
180+
int wildcardIndex = -1;
181+
if (basePathStr.contains("*")) {
182+
wildcardIndex = basePathStr.indexOf('*');
183+
} else if (basePathStr.contains("?")) {
184+
wildcardIndex = basePathStr.indexOf('?');
185+
}
186+
187+
if (wildcardIndex != -1) {
188+
basePathStr = basePathStr.substring(0, wildcardIndex);
189+
// Remove trailing slash or incomplete path segment
190+
int lastSlash = basePathStr.lastIndexOf('/');
191+
if (lastSlash != -1) {
192+
// Resolve the base path relative to npmModuleDir
193+
return npmModuleDir
194+
.resolve(basePathStr.substring(0, lastSlash));
195+
}
196+
}
197+
198+
return npmModuleDir;
199+
}
200+
165201
private List<Path> collectFiles(Path basePath, String matcherPattern) {
166202
final List<Path> filePaths = new ArrayList<>();
167203
if (!basePath.toFile().exists()) {

flow-server/src/test/java/com/vaadin/flow/server/frontend/TaskCopyNpmAssetsFilesTest.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,29 @@ public void assertFolderIsCopied() throws IOException {
111111
.contains("VAADIN/static/assets/button/image.gif"));
112112
}
113113

114+
@Test
115+
public void copiedFolderStructureIsKept() throws IOException {
116+
Mockito.when(scanner.getAssets())
117+
.thenReturn(Map.of("test-button", List.of("**:button")));
118+
119+
TaskCopyNpmAssetsFiles taskCopyNpmAssetsFiles = new TaskCopyNpmAssetsFiles(
120+
options);
121+
taskCopyNpmAssetsFiles.execute();
122+
123+
Set<String> filesInDirectory = getFilesInDirectory(
124+
webappResourcesDirectory);
125+
Assert.assertEquals(3, filesInDirectory.size());
126+
Assert.assertTrue("Could not find file images/image.jpg",
127+
filesInDirectory.contains(
128+
"VAADIN/static/assets/button/images/image.jpg"));
129+
Assert.assertTrue("Could not find file images/image.gif",
130+
filesInDirectory.contains(
131+
"VAADIN/static/assets/button/images/image.gif"));
132+
Assert.assertTrue("Could not find file templates/button.template",
133+
filesInDirectory.contains(
134+
"VAADIN/static/assets/button/templates/button.template"));
135+
}
136+
114137
@Test
115138
public void singleAssertFromFolderIsCopied() throws IOException {
116139
Mockito.when(scanner.getAssets()).thenReturn(

0 commit comments

Comments
 (0)