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

io.quarkiverse.web.bundler.deployment.QuteTemplateWebAssetsProcessor Maven / Gradle / Ivy

There is a newer version: 1.8.0
Show newest version
package io.quarkiverse.web.bundler.deployment;

import static io.quarkiverse.web.bundler.deployment.StaticWebAssetsProcessor.makeWebAssetPublic;
import static io.quarkiverse.web.bundler.deployment.util.PathUtils.prefixWithSlash;
import static java.util.concurrent.CompletableFuture.completedFuture;

import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.net.URL;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;

import org.eclipse.microprofile.config.ConfigProvider;

import io.quarkiverse.web.bundler.deployment.items.GeneratedBundleBuildItem;
import io.quarkiverse.web.bundler.deployment.items.QuteTemplatesBuildItem;
import io.quarkiverse.web.bundler.deployment.items.WebAsset;
import io.quarkiverse.web.bundler.deployment.web.GeneratedWebResourceBuildItem;
import io.quarkiverse.web.bundler.deployment.web.GeneratedWebResourceBuildItem.SourceType;
import io.quarkiverse.web.bundler.runtime.Bundle;
import io.quarkus.deployment.annotations.BuildProducer;
import io.quarkus.deployment.annotations.BuildStep;
import io.quarkus.deployment.builditem.LaunchModeBuildItem;
import io.quarkus.deployment.builditem.LiveReloadBuildItem;
import io.quarkus.qute.*;

public class QuteTemplateWebAssetsProcessor {
    @BuildStep
    void processHtmlTemplateWebAssets(WebBundlerConfig config,
            QuteTemplatesBuildItem htmlTemplates,
            GeneratedBundleBuildItem generatedBundle,
            BuildProducer staticResourceProducer,
            LiveReloadBuildItem liveReload,
            LaunchModeBuildItem launchMode) {
        final Map bundle = generatedBundle != null ? generatedBundle.getBundle() : Map.of();
        final Bundle.Mapping mapping = new Bundle.Mapping() {
            @Override
            public String get(String name) {
                return bundle.get(name);
            }

            @Override
            public Set names() {
                return bundle.keySet();
            }
        };
        final Engine engine = Engine.builder()
                .addDefaults()
                .addNamespaceResolver(NamespaceResolver.builder("inject")
                        .resolve((c) -> c.getName().equals("bundle") ? new Bundle(mapping) : null)
                        .build())
                .addNamespaceResolver(NamespaceResolver.builder("build")
                        .resolve((c) -> c.getName().equals("launchMode") ? launchMode.getLaunchMode().toString() : null)
                        .build())
                .addNamespaceResolver(NamespaceResolver.builder("config")
                        .resolveAsync(this::resolveConfig)
                        .build())
                .addLocator(new WebBundlerTagsLocator())
                .addSectionHelper(new UserTagSectionHelper.Factory("bundle", "web-bundler/bundle.html"))
                .addValueResolver(new ReflectionValueResolver())
                .addParserHook(new Qute.IndexedArgumentsParserHook())
                .addResultMapper(new HtmlEscaper(ImmutableList.of("text/html", "text/xml")))
                .build();
        for (WebAsset webAsset : htmlTemplates.getWebAssets()) {
            final byte[] bytes = webAsset.resource().contentOrReadFromFile();
            final String content = engine.parse(new String(bytes, webAsset.charset())).render();
            makeWebAssetPublic(staticResourceProducer, prefixWithSlash(webAsset.pathFromWebRoot(config.webRoot())),
                    HtmlPageWebAsset.of(webAsset, content), SourceType.BUILD_TIME_TEMPLATE);
        }
    }

    private CompletionStage resolveConfig(EvalContext ctx) {
        List params = ctx.getParams();
        final String name = ctx.getName();
        if (params.isEmpty()) {
            return findConfig(name, String.class);
        }
        if (name.equals("boolean")) {
            return ctx.evaluate(params.get(0)).thenCompose(propertyName -> findConfig(propertyName.toString(), Boolean.class));
        }
        if (name.equals("integer")) {
            return ctx.evaluate(params.get(0)).thenCompose(propertyName -> findConfig(propertyName.toString(), Integer.class));
        }
        return ctx.evaluate(params.get(0)).thenCompose(propertyName -> findConfig(propertyName.toString(), String.class));
    }

    private static  CompletableFuture findConfig(String propertyName, Class type) {
        Optional val = ConfigProvider.getConfig().getOptionalValue(propertyName, type);
        return completedFuture(val.isPresent() ? val.get() : Results.NotFound.from(propertyName));
    }

    private static final class WebBundlerTagsLocator implements TemplateLocator {
        @Override
        public Optional locate(String id) {
            if (!id.startsWith("web-bundler/")) {
                return Optional.empty();
            }
            String name = id.replace("web-bundler/", "");
            final URL resource = this.getClass().getResource("/templates/tags/" + name);
            if (resource == null) {
                return Optional.empty();
            }
            return Optional.of(new TemplateLocation() {
                @Override
                public Reader read() {
                    try {
                        return new InputStreamReader(resource.openStream(), StandardCharsets.UTF_8);
                    } catch (IOException e) {
                        throw new RuntimeException(e);
                    }
                }

                @Override
                public Optional getVariant() {
                    return Optional.empty();
                }
            });
        }

    }

    record HtmlPageWebAsset(String resourceName, byte[] content, Charset charset) implements WebAsset {

        static HtmlPageWebAsset of(WebAsset sourceAsset, String content) {
            return new HtmlPageWebAsset(sourceAsset.resourceName(), content.getBytes(sourceAsset.charset()),
                    sourceAsset.charset());
        }

        @Override
        public Resource resource() {
            return new Resource(content());
        }

        @Override
        public Optional srcFilePath() {
            return Optional.empty();
        }
    }
}