All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.helidon.build.maven.sitegen.VuetifyBackend Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 2018, 2025 Oracle and/or its affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package io.helidon.build.maven.sitegen;

import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Deque;
import java.util.HashMap;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Objects;
import java.util.function.Supplier;
import java.util.stream.Stream;

import io.helidon.build.common.SourcePath;
import io.helidon.build.maven.sitegen.freemarker.FreemarkerEngine;
import io.helidon.build.maven.sitegen.freemarker.TemplateSession;
import io.helidon.build.maven.sitegen.models.Nav;
import io.helidon.build.maven.sitegen.models.Page;

import static io.helidon.build.common.FileUtils.resourceAsPath;
import static io.helidon.build.common.Strings.requireValid;
import static io.helidon.build.maven.sitegen.Context.copyResources;
import static java.util.stream.Collectors.toList;
import static java.util.stream.Collectors.toMap;

/**
 * A backend implementation for Vuetify.
 *
 * @see https://vuetifyjs.com
 */
public class VuetifyBackend extends Backend {

    /**
     * The Vuetify backend name.
     */
    public static final String BACKEND_NAME = "vuetify";

    private final Nav nav;
    private final Map theme;
    private final Path staticFiles;
    private final String home;
    private final List releases;

    private VuetifyBackend(Builder builder) {
        super(BACKEND_NAME);
        theme = builder.theme;
        nav = builder.nav;
        home = requireValid(builder.home, "home is invalid!");
        releases = builder.releases;
        staticFiles = resourceAsPath("/files/vuetify", VuetifyBackend.class);
    }

    /**
     * Get the navigation tree root.
     *
     * @return navigation tree root or {@code null} if not set
     */
    public Nav nav() {
        return nav;
    }

    /**
     * Get the theme.
     *
     * @return map, never {@code null}
     */
    public Map theme() {
        return theme;
    }

    /**
     * Get the home.
     *
     * @return home or {@code null} if not set
     */
    public String home() {
        return home;
    }

    /**
     * Get the releases.
     *
     * @return list, never {@code null}
     */
    public List releases() {
        return releases;
    }

    @Override
    public void generate(Context ctx) {
        Map pages = ctx.pages();
        Page home = pages.get(new SourcePath(this.home).asString(false));
        if (home == null) {
            throw new IllegalStateException("unable to get home page");
        }

        // resolve navigation
        Nav resolvedNav;
        List navRoutes;
        if (nav != null) {
            resolvedNav = resolveNav(ctx);
            navRoutes = resolvedNav.items()
                                   .stream()
                                   .flatMap(n -> n.items().isEmpty() ? Stream.of(n) : n.items().stream()) // depth:1
                                   .flatMap(n -> n.items().isEmpty() ? Stream.of(n) : n.items().stream()) // depth:2
                                   .flatMap(n -> n.items().isEmpty() ? Stream.of(n) : n.items().stream()) // depth:3
                                   .map(Nav::to)
                                   .filter(Objects::nonNull)
                                   .collect(toList());
        } else {
            resolvedNav = null;
            navRoutes = List.of();
        }

        Path pagesDir = ctx.outputDir().resolve("pages");
        try {
            Files.createDirectories(pagesDir);
        } catch (IOException ex) {
            throw new UncheckedIOException(ex);
        }

        // render all pages
        ctx.processPages(pagesDir, "js");

        // copy declared assets
        ctx.copyStaticAssets();

        TemplateSession session = ctx.templateSession();

        Map pagesByRoute = pages.values()
                                              .stream()
                                              .map(p -> Map.entry(p.target(), p))
                                              .collect(toMap(Map.Entry::getKey, Map.Entry::getValue));

        // resolve all routes
        List routes = new ArrayList<>();
        routes.add(home.target());
        navRoutes.forEach(r -> {
            if (!routes.contains(r)) {
                routes.add(r);
            }
        });
        pagesByRoute.keySet().forEach(r -> {
            if (!routes.contains(r)) {
                routes.add(r);
            }
        });

        Map allBindings = session.vueBindings().bindings();

        Map model = new HashMap<>();
        model.put("searchEntries", session.searchIndex().entries());
        model.put("navRoutes", navRoutes);
        model.put("allRoutes", routes);
        model.put("customLayoutEntries", session.customLayouts().mappings());
        model.put("pages", pagesByRoute);
        model.put("metadata", home.metadata());
        model.put("nav", resolvedNav);
        model.put("header", ctx.site().header());
        model.put("theme", theme);
        model.put("home", home);
        model.put("releases", releases);
        model.put("bindings", allBindings);

        FreemarkerEngine freemarker = ctx.site().engine().freemarker();
        Path outputDir = ctx.outputDir();

        // custom bindings
        for (Page page : pages.values()) {
            String bindings = allBindings.get(page.source());
            if (bindings != null) {
                Map bindingsModel = new HashMap<>(model);
                bindingsModel.put("bindings", bindings);
                bindingsModel.put("page", page);
                String path = "pages/" + page.target() + "_custom.js";
                freemarker.renderFile("custom_bindings", path, bindingsModel, outputDir);
            }
        }

        // render search-index.js
        freemarker.renderFile("search_index", "main/search-index.json", model, outputDir);

        // render index.html
        freemarker.renderFile("index", "index.html", model, outputDir);

        // render main/config.js
        freemarker.renderFile("config", "main/config.js", model, outputDir);

        // copy vuetify resources
        copyResources(staticFiles, ctx.outputDir());
    }

    private Nav resolveNav(Context ctx) {
        Map pages = ctx.pages();
        Deque builders = new ArrayDeque<>();
        Deque




© 2015 - 2025 Weber Informatics LLC | Privacy Policy