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 0924369e56c..9b33f013c16 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 @@ -426,7 +426,7 @@ public class PluginEffectiveConfiguration( .convention(false) public val reactEnable: Provider = extension.reactEnable - .convention(FrontendUtils.isReactRouterRequired(BuildFrontendUtil.getFrontendDirectory(GradlePluginAdapter(project, this, true)))) + .convention(FrontendUtils.isReactRouterRequired(BuildFrontendUtil.getFrontendDirectory(GradlePluginAdapter(project, this, true)), File(projectBuildDir.get()))) .overrideWithSystemPropertyFlag(InitParameters.REACT_ENABLE) public val cleanFrontendFiles: Property = extension.cleanFrontendFiles 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 871f78cabcd..388e82372c8 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 @@ -529,7 +529,8 @@ public boolean isReactEnabled() { return reactEnable; } File frontendDirectory = BuildFrontendUtil.getFrontendDirectory(this); - return FrontendUtils.isReactRouterRequired(frontendDirectory); + return FrontendUtils.isReactRouterRequired(frontendDirectory, + new File(projectBaseDirectory().toFile(), buildFolder())); } @Override diff --git a/flow-server/src/main/java/com/vaadin/flow/server/frontend/FrontendUtils.java b/flow-server/src/main/java/com/vaadin/flow/server/frontend/FrontendUtils.java index f8935dde512..b452ddefecd 100644 --- a/flow-server/src/main/java/com/vaadin/flow/server/frontend/FrontendUtils.java +++ b/flow-server/src/main/java/com/vaadin/flow/server/frontend/FrontendUtils.java @@ -33,6 +33,7 @@ import java.util.Map; import java.util.Objects; import java.util.Optional; +import java.util.Properties; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import java.util.regex.Matcher; @@ -1266,7 +1267,8 @@ public static File getFlowGeneratedWebComponentsFolder( * path to the frontend folder in a project. * @return {@code false} if vaadin-router is used, {@code true} otherwise. */ - public static boolean isReactRouterRequired(File frontendDirectory) { + public static boolean isReactRouterRequired(File frontendDirectory, + File buildDirectory) { Objects.requireNonNull(frontendDirectory); boolean result = true; File indexTs = new File(frontendDirectory, FrontendUtils.INDEX_TS); @@ -1282,6 +1284,27 @@ public static boolean isReactRouterRequired(File frontendDirectory) { e); } } + if (result) { + // If we are enabling react we should for spring projects check the + // application.properties file flag also. + File resource = new File(buildDirectory, + "classes/application.properties"); + if (resource.exists()) { + try (InputStream resourceStream = resource.toURL() + .openStream()) { + Properties prop = new Properties(); + prop.load(resourceStream); + if (prop.containsKey("vaadin.react.enable")) { + result = Boolean.getBoolean( + prop.get("vaadin.react.enable").toString()); + } + } catch (IOException e) { + getLogger().error( + "Couldn't read application.properties for react flag, react-router will be used", + e); + } + } + } if (getLogger().isDebugEnabled()) { getLogger().debug("Auto-detected client-side router to use: {}", result ? "react-router" : "vaadin-router"); @@ -1289,6 +1312,10 @@ public static boolean isReactRouterRequired(File frontendDirectory) { return result; } + protected static ClassLoader getClassLoader() { + return FrontendUtils.class.getClassLoader(); + } + /** * Auto-detects if hilla views are used in the project based on what is in * routes.ts or routes.tsx file. 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 db72b6a31bb..60eb89c0c8e 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 @@ -930,8 +930,9 @@ public boolean isReactEnabled() { public Options withReact(boolean reactEnable) { this.reactEnable = reactEnable; - if (reactEnable && !FrontendUtils - .isReactRouterRequired(getFrontendDirectory())) { + if (reactEnable + && !FrontendUtils.isReactRouterRequired(getFrontendDirectory(), + getBuildDirectory())) { LoggerFactory.getLogger(Options.class).debug( "Setting reactEnable to false as Vaadin Router is used!"); this.reactEnable = false; diff --git a/flow-server/src/main/java/com/vaadin/flow/server/frontend/scanner/FrontendDependenciesScanner.java b/flow-server/src/main/java/com/vaadin/flow/server/frontend/scanner/FrontendDependenciesScanner.java index ce929c9360a..8f292879d72 100644 --- a/flow-server/src/main/java/com/vaadin/flow/server/frontend/scanner/FrontendDependenciesScanner.java +++ b/flow-server/src/main/java/com/vaadin/flow/server/frontend/scanner/FrontendDependenciesScanner.java @@ -111,7 +111,8 @@ public FrontendDependenciesScanner createScanner( public FrontendDependenciesScanner createScanner(Options options) { boolean reactEnabled = options.isReactEnabled() && FrontendUtils - .isReactRouterRequired(options.getFrontendDirectory()); + .isReactRouterRequired(options.getFrontendDirectory(), + options.getBuildDirectory()); return createScanner(!options.isUseByteCodeScanner(), options.getClassFinder(), options.isGenerateEmbeddableWebComponents(), diff --git a/flow-server/src/test/java/com/vaadin/flow/server/frontend/FrontendUtilsTest.java b/flow-server/src/test/java/com/vaadin/flow/server/frontend/FrontendUtilsTest.java index 3c629362fab..b2f19c99d67 100644 --- a/flow-server/src/test/java/com/vaadin/flow/server/frontend/FrontendUtilsTest.java +++ b/flow-server/src/test/java/com/vaadin/flow/server/frontend/FrontendUtilsTest.java @@ -33,6 +33,7 @@ import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; +import org.mockito.MockedStatic; import org.mockito.Mockito; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -505,7 +506,7 @@ public void isReactRouterRequired_importsVaadinRouter_false() router.setRoutes(routes); """); Assert.assertFalse("vaadin-router expected when it imported", - FrontendUtils.isReactRouterRequired(frontend)); + FrontendUtils.isReactRouterRequired(frontend, frontend)); } @Test @@ -521,14 +522,52 @@ public void isReactRouterRequired_doesntImportVaadinRouter_true() """); Assert.assertTrue( "react-router expected when no vaadin-router imported", - FrontendUtils.isReactRouterRequired(frontend)); + FrontendUtils.isReactRouterRequired(frontend, frontend)); } @Test public void isReactRouterRequired_noIndexTsFile_true() throws IOException { File frontend = tmpDir.newFolder(FrontendUtils.DEFAULT_FRONTEND_DIR); Assert.assertTrue("react-router expected when index.ts isn't there", - FrontendUtils.isReactRouterRequired(frontend)); + FrontendUtils.isReactRouterRequired(frontend, frontend)); + } + + @Test + public void isReactRouterRequired_applicationPropertiesHasFalse_false() + throws IOException { + File frontend = prepareFrontendForRoutesFile(FrontendUtils.INDEX_TS, + """ + import { createElement } from "react"; + import { createRoot } from "react-dom/client"; + import App from "./App.js"; + + createRoot(document.getElementById("outlet")!).render(createElement(App)); + """); + File applicationProperties = new File(frontend, + "classes/application.properties"); + FileUtils.write(applicationProperties, "vaadin.react.enable=false"); + Assert.assertFalse( + "react-router expected when no vaadin-router imported", + FrontendUtils.isReactRouterRequired(frontend, frontend)); + } + + @Test + public void isReactRouterRequired_applicationPropertiesTrueDoesNotOverride_false() + throws IOException { + File frontend = prepareFrontendForRoutesFile(FrontendUtils.INDEX_TS, + """ + import { Router } from '@vaadin/router'; + import { routes } from './routes'; + + export const router = new Router(document.querySelector('#outlet')); + router.setRoutes(routes); + """); + File applicationProperties = new File(frontend, + "classes/application.properties"); + FileUtils.write(applicationProperties, "vaadin.react.enable=true"); + Assert.assertFalse( + "react-router expected when no vaadin-router imported", + FrontendUtils.isReactRouterRequired(frontend, frontend)); } @Test diff --git a/flow-server/src/test/java/com/vaadin/flow/server/frontend/NodeUpdaterTest.java b/flow-server/src/test/java/com/vaadin/flow/server/frontend/NodeUpdaterTest.java index 704126a6751..003f4da8ad7 100644 --- a/flow-server/src/test/java/com/vaadin/flow/server/frontend/NodeUpdaterTest.java +++ b/flow-server/src/test/java/com/vaadin/flow/server/frontend/NodeUpdaterTest.java @@ -568,9 +568,9 @@ public void getDefaultDependencies_reactIsUsed_addsHillaReactComponents() { .mockStatic(FrontendUtils.class)) { mock.when(() -> FrontendUtils.isHillaUsed(Mockito.any(File.class), Mockito.any(ClassFinder.class))).thenReturn(true); - mock.when(() -> FrontendUtils - .isReactRouterRequired(Mockito.any(File.class))) - .thenReturn(true); + mock.when(() -> FrontendUtils.isReactRouterRequired( + options.getFrontendDirectory(), + options.getBuildDirectory())).thenReturn(true); options.withReact(true); Map defaultDeps = nodeUpdater .getDefaultDependencies(); diff --git a/vaadin-dev-server/src/main/java/com/vaadin/base/devserver/startup/DevModeInitializer.java b/vaadin-dev-server/src/main/java/com/vaadin/base/devserver/startup/DevModeInitializer.java index 67b2bc729b9..cf385544510 100644 --- a/vaadin-dev-server/src/main/java/com/vaadin/base/devserver/startup/DevModeInitializer.java +++ b/vaadin-dev-server/src/main/java/com/vaadin/base/devserver/startup/DevModeInitializer.java @@ -268,8 +268,9 @@ public static DevModeHandler initDevModeHandler(Set> classes, JsonObject tokenFileData = Json.createObject(); Mode mode = config.getMode(); boolean reactEnable = config.getBooleanProperty(REACT_ENABLE, - FrontendUtils - .isReactRouterRequired(options.getFrontendDirectory())); + FrontendUtils.isReactRouterRequired( + options.getFrontendDirectory(), + options.getBuildDirectory())); options.enablePackagesUpdate(true) .useByteCodeScanner(useByteCodeScanner) .withFrontendGeneratedFolder(frontendGeneratedFolder)