Skip to content

Commit

Permalink
feat: generate layout.json (#19933)
Browse files Browse the repository at this point in the history
Genrate a layout.json for
Hilla to automatically
enable server layouts

part of vaadin/hilla#2709
  • Loading branch information
caalador authored Sep 13, 2024
1 parent 20623f5 commit e4a00ab
Show file tree
Hide file tree
Showing 4 changed files with 56 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.util.Set;
import java.util.regex.Pattern;

import org.apache.commons.io.FileUtils;
Expand All @@ -28,10 +29,14 @@
import org.slf4j.LoggerFactory;

import com.vaadin.flow.internal.StringUtil;
import com.vaadin.flow.router.Layout;
import com.vaadin.flow.router.Route;
import com.vaadin.flow.server.ExecutionFailedException;
import com.vaadin.flow.server.Version;

import elemental.json.Json;
import elemental.json.JsonArray;
import elemental.json.JsonObject;
import static com.vaadin.flow.server.frontend.FileIOUtils.compareIgnoringIndentationEOLAndWhiteSpace;
import static java.nio.charset.StandardCharsets.UTF_8;

Expand Down Expand Up @@ -102,6 +107,7 @@ function build() {
private static final String VAADIN_REACT_TSX = "vaadin-react.tsx";
private static final String REACT_ADAPTER_TEMPLATE = "ReactAdapter.template";
private static final String REACT_ADAPTER_TSX = "ReactAdapter.tsx";
private static final String LAYOUTS_JSON = "layouts.json";
static final String FLOW_FLOW_TSX = "flow/" + FLOW_TSX;
static final String FLOW_REACT_ADAPTER_TSX = "flow/" + REACT_ADAPTER_TSX;
private static final String ROUTES_JS_IMPORT_PATH_TOKEN = "%routesJsImportPath%";
Expand Down Expand Up @@ -158,6 +164,8 @@ private void doExecute() throws ExecutionFailedException {
writeFile(flowTsx, getFileContent(FLOW_TSX));
writeFile(vaadinReactTsx,
getVaadinReactTsContent(routesTsx.exists()));
writeFile(new File(frontendGeneratedFolder, LAYOUTS_JSON),
layoutsContent());
if (fileAvailable(REACT_ADAPTER_TEMPLATE)) {
String reactAdapterContent = getFileContent(
REACT_ADAPTER_TEMPLATE);
Expand Down Expand Up @@ -193,6 +201,19 @@ && serverRoutesAvailable()) {
}
}

private String layoutsContent() {
JsonArray availableLayouts = Json.createArray();
Set<Class<?>> layoutClasses = options.getClassFinder()
.getAnnotatedClasses(Layout.class);
for (Class<?> layout : layoutClasses) {
JsonObject layoutObject = Json.createObject();
layoutObject.put("path",
layout.getAnnotation(Layout.class).value());
availableLayouts.set(availableLayouts.length(), layoutObject);
}
return availableLayouts.toJson();
}

private void cleanup() throws ExecutionFailedException {
try {
File frontendDirectory = options.getFrontendDirectory();
Expand All @@ -204,7 +225,9 @@ private void cleanup() throws ExecutionFailedException {
FLOW_REACT_ADAPTER_TSX);
File frontendGeneratedFolderRoutesTsx = new File(
frontendGeneratedFolder, FrontendUtils.ROUTES_TSX);
File layoutsJson = new File(frontendGeneratedFolder, LAYOUTS_JSON);
FileUtils.deleteQuietly(flowTsx);
FileUtils.deleteQuietly(layoutsJson);
FileUtils.deleteQuietly(vaadinReactTsx);
FileUtils.deleteQuietly(reactAdapterTsx);
FileUtils.deleteQuietly(frontendGeneratedFolderRoutesTsx);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,9 @@

import com.vaadin.flow.component.Component;
import com.vaadin.flow.component.Tag;
import com.vaadin.flow.router.Layout;
import com.vaadin.flow.router.Route;
import com.vaadin.flow.router.RouterLayout;
import com.vaadin.flow.server.ExecutionFailedException;
import com.vaadin.flow.server.frontend.scanner.ClassFinder;
import com.vaadin.tests.util.MockOptions;
Expand Down Expand Up @@ -82,6 +84,27 @@ public void reactFilesAreWrittenToFrontend()
"routes.tsx").exists());
Assert.assertFalse("Missing ./frontend/routes.tsx",
new File(frontend, "routes.tsx").exists());
Assert.assertTrue(
"Missing ./frontend/" + FrontendUtils.GENERATED
+ "layouts.json",
new File(new File(frontend, FrontendUtils.GENERATED),
"layouts.json").exists());
}

@Test
public void layoutsJson_containsExpectedPaths()
throws ExecutionFailedException, IOException {
Mockito.when(options.getClassFinder().getAnnotatedClasses(Layout.class))
.thenReturn(Collections.singleton(TestLayout.class));

TaskGenerateReactFiles task = new TaskGenerateReactFiles(options);
task.execute();

String layoutsContent = FileUtils.readFileToString(
new File(options.getFrontendGeneratedFolder(), "layouts.json"));

Assert.assertEquals("[{\"path\":\"/test\"}]", layoutsContent);

}

@Test
Expand Down Expand Up @@ -555,4 +578,9 @@ public void routesContainExport_oneSingleExport_exceptionThrown()
@Route("test")
private class TestRoute extends Component {
}

@Tag("div")
@Layout("/test")
private class TestLayout extends Component implements RouterLayout {
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
import com.vaadin.flow.internal.DevModeHandlerManager;
import com.vaadin.flow.internal.Template;
import com.vaadin.flow.router.HasErrorParameter;
import com.vaadin.flow.router.Layout;
import com.vaadin.flow.router.Route;
import com.vaadin.flow.server.LoadDependenciesOnStartup;
import com.vaadin.flow.server.PWA;
Expand Down Expand Up @@ -68,7 +69,7 @@
JavaScript.Container.class, Theme.class, NoTheme.class,
HasErrorParameter.class, PWA.class, AppShellConfigurator.class,
Template.class, LoadDependenciesOnStartup.class,
TypeScriptBootstrapModifier.class, Component.class })
TypeScriptBootstrapModifier.class, Component.class, Layout.class })
@WebListener
public class DevModeStartupListener
implements VaadinServletContextStartupInitializer, Serializable,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
import com.vaadin.flow.component.page.AppShellConfigurator;
import com.vaadin.flow.internal.Template;
import com.vaadin.flow.router.HasErrorParameter;
import com.vaadin.flow.router.Layout;
import com.vaadin.flow.router.Route;
import com.vaadin.flow.server.LoadDependenciesOnStartup;
import com.vaadin.flow.server.PWA;
Expand Down Expand Up @@ -69,7 +70,8 @@ public void applicableClasses_knownClasses() {
CssImport.Container.class, Theme.class, NoTheme.class,
HasErrorParameter.class, PWA.class, AppShellConfigurator.class,
Template.class, LoadDependenciesOnStartup.class,
Component.class, TypeScriptBootstrapModifier.class);
Component.class, TypeScriptBootstrapModifier.class,
Layout.class);

for (Class<?> clz : classes) {
assertTrue("should be a known class " + clz.getName(),
Expand Down

0 comments on commit e4a00ab

Please sign in to comment.