diff --git a/flow-plugins/flow-gradle-plugin/src/main/kotlin/com/vaadin/gradle/VaadinFlowPluginExtension.kt b/flow-plugins/flow-gradle-plugin/src/main/kotlin/com/vaadin/gradle/VaadinFlowPluginExtension.kt index b95b14e9adb..9f922d5c5d6 100644 --- a/flow-plugins/flow-gradle-plugin/src/main/kotlin/com/vaadin/gradle/VaadinFlowPluginExtension.kt +++ b/flow-plugins/flow-gradle-plugin/src/main/kotlin/com/vaadin/gradle/VaadinFlowPluginExtension.kt @@ -278,6 +278,13 @@ public abstract class VaadinFlowPluginExtension @Inject constructor(private val public abstract val cleanFrontendFiles: Property + /** + * The list of extra file extensions that are considered project files. + * Hashes are calculated for these files as part of detecting if a new prod + * bundle should be generated. + */ + public abstract val extraProjectFileExtensions: ListProperty + public fun filterClasspath(@DelegatesTo(value = ClasspathFilter::class, strategy = Closure.DELEGATE_FIRST) block: Closure<*>) { block.delegate = classpathFilter block.resolveStrategy = Closure.DELEGATE_FIRST @@ -427,6 +434,10 @@ public class PluginEffectiveConfiguration( public val cleanFrontendFiles: Property = extension.cleanFrontendFiles .convention(true) + + public val extraProjectFileExtensions: ListProperty = extension.extraProjectFileExtensions + .convention(emptyList()) + /** * Finds the value of a boolean property. It searches in gradle and system properties. * @@ -476,6 +487,7 @@ public class PluginEffectiveConfiguration( "frontendHotdeploy=${frontendHotdeploy.get()}," + "reactEnable=${reactEnable.get()}," + "cleanFrontendFiles=${cleanFrontendFiles.get()}" + + "extraProjectFileExtensions=${extraProjectFileExtensions.get()}" + ")" public companion object { public fun get(project: Project): PluginEffectiveConfiguration = diff --git a/flow-plugins/flow-maven-plugin/src/main/java/com/vaadin/flow/plugin/maven/FlowModeAbstractMojo.java b/flow-plugins/flow-maven-plugin/src/main/java/com/vaadin/flow/plugin/maven/FlowModeAbstractMojo.java index fa50c77183a..0228a18f38b 100644 --- a/flow-plugins/flow-maven-plugin/src/main/java/com/vaadin/flow/plugin/maven/FlowModeAbstractMojo.java +++ b/flow-plugins/flow-maven-plugin/src/main/java/com/vaadin/flow/plugin/maven/FlowModeAbstractMojo.java @@ -25,6 +25,7 @@ import java.net.URISyntaxException; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.Collections; import java.util.List; import java.util.Set; import java.util.function.Function; @@ -232,6 +233,9 @@ public abstract class FlowModeAbstractMojo extends AbstractMojo @Parameter(property = InitParameters.REACT_ENABLE, defaultValue = "${null}") private Boolean reactEnable; + @Parameter(defaultValue = "${null}") + private List extraProjectFileExtensions; + /** * Generates a List of ClasspathElements (Run and CompileTime) from a * MavenProject. @@ -522,4 +526,14 @@ public boolean isReactEnabled() { File frontendDirectory = BuildFrontendUtil.getFrontendDirectory(this); return FrontendUtils.isReactRouterRequired(frontendDirectory); } + + @Override + public List extraProjectFileExtensions() { + if (extraProjectFileExtensions != null) { + return extraProjectFileExtensions; + } + + return Collections.emptyList(); + } + } diff --git a/flow-plugins/flow-plugin-base/src/main/java/com/vaadin/flow/plugin/base/BuildFrontendUtil.java b/flow-plugins/flow-plugin-base/src/main/java/com/vaadin/flow/plugin/base/BuildFrontendUtil.java index 27a3898c39b..cc6c0654f70 100644 --- a/flow-plugins/flow-plugin-base/src/main/java/com/vaadin/flow/plugin/base/BuildFrontendUtil.java +++ b/flow-plugins/flow-plugin-base/src/main/java/com/vaadin/flow/plugin/base/BuildFrontendUtil.java @@ -163,7 +163,9 @@ public static void prepareFrontend(PluginAdapterBase adapter) .setNodeAutoUpdate(adapter.nodeAutoUpdate()) .withHomeNodeExecRequired(adapter.requireHomeNodeExec()) .setJavaResourceFolder(adapter.javaResourceFolder()) - .withProductionMode(false).withReact(adapter.isReactEnabled()); + .withProductionMode(false).withReact(adapter.isReactEnabled()) + .withExtraProjectFileExtensions( + adapter.extraProjectFileExtensions()); // Copy jar artifact contents in TaskCopyFrontendFiles options.copyResources(adapter.getJarFiles()); @@ -403,7 +405,9 @@ public static void runDevBuildNodeUpdater(PluginAdapterBuild adapter) .withBundleBuild(true) .skipDevBundleBuild(adapter.skipDevBundleBuild()) .withCompressBundle(adapter.compressBundle()) - .withReact(adapter.isReactEnabled()); + .withReact(adapter.isReactEnabled()) + .withExtraProjectFileExtensions( + adapter.extraProjectFileExtensions()); new NodeTasks(options).execute(); } catch (ExecutionFailedException exception) { throw exception; diff --git a/flow-plugins/flow-plugin-base/src/main/java/com/vaadin/flow/plugin/base/PluginAdapterBase.java b/flow-plugins/flow-plugin-base/src/main/java/com/vaadin/flow/plugin/base/PluginAdapterBase.java index e0a187cb10e..7775019fc7b 100644 --- a/flow-plugins/flow-plugin-base/src/main/java/com/vaadin/flow/plugin/base/PluginAdapterBase.java +++ b/flow-plugins/flow-plugin-base/src/main/java/com/vaadin/flow/plugin/base/PluginAdapterBase.java @@ -326,4 +326,11 @@ default Lookup createLookup(ClassFinder classFinder) { * router and excluding React dependencies */ boolean isReactEnabled(); + + /** + * Get the list of project file extensions. + * + * @return list of project file extensions + */ + List extraProjectFileExtensions(); } diff --git a/flow-server/src/main/java/com/vaadin/flow/server/frontend/Options.java b/flow-server/src/main/java/com/vaadin/flow/server/frontend/Options.java index 4d6dc35ca02..badf2accc1e 100644 --- a/flow-server/src/main/java/com/vaadin/flow/server/frontend/Options.java +++ b/flow-server/src/main/java/com/vaadin/flow/server/frontend/Options.java @@ -82,6 +82,8 @@ public class Options implements Serializable { private boolean compressBundle = true; + private List extraProjectFileExtensions = null; + /** * The node.js version to be used when node.js is installed automatically by * Vaadin, for example "v16.0.0". Defaults to @@ -954,4 +956,26 @@ public Options withCleanOldGeneratedFiles(boolean clean) { public boolean isCleanOldGeneratedFiles() { return cleanOldGeneratedFiles; } + + /** + * Sets the extra project file extensions. + * + * @param extraProjectFileExtensions + * the project file extensions + * @return this builder + */ + public Options withExtraProjectFileExtensions( + List extraProjectFileExtensions) { + this.extraProjectFileExtensions = extraProjectFileExtensions; + return this; + } + + /** + * Gets the project file extensions. + * + * @return the project file extensions + */ + public List getExtraProjectFileExtensions() { + return extraProjectFileExtensions; + } } diff --git a/flow-server/src/main/java/com/vaadin/flow/server/frontend/TaskUpdateVite.java b/flow-server/src/main/java/com/vaadin/flow/server/frontend/TaskUpdateVite.java index e76b2e62f3d..8bf2ff7a30a 100644 --- a/flow-server/src/main/java/com/vaadin/flow/server/frontend/TaskUpdateVite.java +++ b/flow-server/src/main/java/com/vaadin/flow/server/frontend/TaskUpdateVite.java @@ -22,10 +22,14 @@ import java.io.UncheckedIOException; import java.net.URL; import java.nio.charset.StandardCharsets; +import java.util.Collections; +import java.util.Optional; import java.util.Set; +import java.util.stream.Collectors; import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; +import org.atmosphere.util.StringEscapeUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -127,7 +131,17 @@ private void createGeneratedConfig() throws IOException { .replace("#webComponentTags#", webComponentTags == null || webComponentTags.isEmpty() ? "" - : String.join(";", webComponentTags)); + : String.join(";", webComponentTags)) + .replace("#extraProjectFileExtensions#", Optional + .ofNullable(options.getExtraProjectFileExtensions()) + .orElse(Collections.emptyList()).stream().map(ext -> { + try { + return "'" + StringEscapeUtils.escapeJava(ext) + + "'"; + } catch (Exception e) { + throw new RuntimeException(e); + } + }).collect(Collectors.joining(", "))); template = updateFileSystemRouterVitePlugin(template); FileIOUtils.writeIfChanged(generatedConfigFile, template); diff --git a/flow-server/src/main/resources/vite.generated.ts b/flow-server/src/main/resources/vite.generated.ts index fa9cf0246af..7e4b6e84638 100644 --- a/flow-server/src/main/resources/vite.generated.ts +++ b/flow-server/src/main/resources/vite.generated.ts @@ -298,7 +298,7 @@ function statsExtracterPlugin(): PluginOption { const frontendFiles: Record = {}; - const projectFileExtensions = ['.js', '.js.map', '.ts', '.ts.map', '.tsx', '.tsx.map', '.css', '.css.map']; + const projectFileExtensions = ['.js', '.js.map', '.ts', '.ts.map', '.tsx', '.tsx.map', '.css', '.css.map'#extraProjectFileExtensions#]; const isThemeComponentsResource = (id: string) => id.startsWith(themeOptions.frontendGeneratedFolder.replace(/\\/g, '/'))