From 48ed0f428786b823ddfc0a381be44e94cc7c5f55 Mon Sep 17 00:00:00 2001 From: Andy Damevin Date: Fri, 12 Jan 2024 15:50:16 +0100 Subject: [PATCH] Add config option and warning message --- .../BundleWebAssetsScannerProcessor.java | 19 ++++++-- ...> QuteTemplateAssetsScannerProcessor.java} | 12 ++--- .../bundler/deployment/WebBundlerConfig.java | 21 ++++++-- .../deployment/items/EntryPointBuildItem.java | 9 ++++ .../items/HtmlTemplatesBuildItem.java | 10 ---- .../items/QuteTemplatesBuildItem.java | 10 ++++ .../GeneratedStaticResourcesProcessor.java | 47 ++++++++++-------- deployment/pom.xml | 16 ++----- ...va => QuteTemplateWebAssetsProcessor.java} | 42 ++++++++-------- .../deployment/WebBundlerProcessor.java | 24 +++++----- .../WebDependenciesScannerProcessor.java | 23 ++++++++- .../deployment/util/ConfiguredPathsTest.java | 30 ------------ .../bundler/test/WebBundlerDevModeTest.java | 48 +++++++++++++++---- .../web/bundler/test/WebBundlerTest.java | 46 +++++++++++++++--- .../resources/META-INF/resources/test.txt | 0 .../src/test/resources/application.properties | 2 + deployment/src/test/resources/dev/app/app.css | 1 + deployment/src/test/resources/dev/app/app.js | 1 + .../src/test/resources/dev/app/other.scss | 1 + deployment/src/test/resources/dev/index.html | 1 + .../src/test/resources/dev/static/hello.txt | 1 + deployment/src/test/resources/web/app/app.css | 1 + deployment/src/test/resources/web/app/app.js | 5 ++ .../src/test/resources/web/app/other.scss | 1 + deployment/src/test/resources/web/index.html | 3 ++ .../src/test/resources/web/static/hello.txt | 1 + docs/modules/ROOT/pages/advanced-guides.adoc | 9 ++++ .../pages/includes/quarkus-web-bundler.adoc | 26 ++++++++-- integration-tests/.locker/pom.xml | 25 ++++++---- integration-tests/pom.xml | 7 ++- pom.xml | 2 +- .../WebBundlerQuteComponentsProcessor.java | 12 ++--- .../BundleRedirectHandlerRecorder.java | 26 ++++++++++ .../WebDependenciesBlockerRecorder.java | 16 ------- .../src/main/resources/application.properties | 1 + 35 files changed, 323 insertions(+), 176 deletions(-) rename common-deployment/src/main/java/io/quarkiverse/web/bundler/deployment/{HtmlTemplateAssetsScannerProcessor.java => QuteTemplateAssetsScannerProcessor.java} (81%) delete mode 100644 common-deployment/src/main/java/io/quarkiverse/web/bundler/deployment/items/HtmlTemplatesBuildItem.java create mode 100644 common-deployment/src/main/java/io/quarkiverse/web/bundler/deployment/items/QuteTemplatesBuildItem.java rename deployment/src/main/java/io/quarkiverse/web/bundler/deployment/{HtmlTemplateWebAssetsProcessor.java => QuteTemplateWebAssetsProcessor.java} (86%) delete mode 100644 deployment/src/test/java/io/quarkiverse/web/bundler/deployment/util/ConfiguredPathsTest.java create mode 100644 deployment/src/test/resources/META-INF/resources/test.txt create mode 100644 deployment/src/test/resources/application.properties create mode 100644 deployment/src/test/resources/dev/app/app.css create mode 100644 deployment/src/test/resources/dev/app/app.js create mode 100644 deployment/src/test/resources/dev/app/other.scss create mode 100644 deployment/src/test/resources/dev/index.html create mode 100644 deployment/src/test/resources/dev/static/hello.txt create mode 100644 deployment/src/test/resources/web/app/app.css create mode 100644 deployment/src/test/resources/web/app/app.js create mode 100644 deployment/src/test/resources/web/app/other.scss create mode 100644 deployment/src/test/resources/web/index.html create mode 100644 deployment/src/test/resources/web/static/hello.txt create mode 100644 runtime/src/main/java/io/quarkiverse/web/bundler/runtime/BundleRedirectHandlerRecorder.java delete mode 100644 runtime/src/main/java/io/quarkiverse/web/bundler/runtime/WebDependenciesBlockerRecorder.java diff --git a/common-deployment/src/main/java/io/quarkiverse/web/bundler/deployment/BundleWebAssetsScannerProcessor.java b/common-deployment/src/main/java/io/quarkiverse/web/bundler/deployment/BundleWebAssetsScannerProcessor.java index a36ed66..28d68ba 100644 --- a/common-deployment/src/main/java/io/quarkiverse/web/bundler/deployment/BundleWebAssetsScannerProcessor.java +++ b/common-deployment/src/main/java/io/quarkiverse/web/bundler/deployment/BundleWebAssetsScannerProcessor.java @@ -10,6 +10,7 @@ import java.util.Map; import java.util.Optional; import java.util.Set; +import java.util.function.Consumer; import java.util.function.Predicate; import java.util.stream.Collectors; @@ -141,15 +142,23 @@ void produceWebAssets(BuildProducer bundles, WebAssetsLookupDevContext context, boolean checkIfExists) { for (Map.Entry> e : context.bundleAssets().entrySet()) { - bundles.produce(new EntryPointBuildItem(e.getKey(), checkIfExists ? checkWebAssets(e.getValue()) : e.getValue())); + produceWebAssetsWithCheck(checkIfExists, e.getValue(), + webAssets -> bundles.produce(new EntryPointBuildItem(e.getKey(), webAssets))); } - bundleConfigAssets.produce(new BundleConfigAssetsBuildItem( - checkIfExists ? checkWebAssets(context.bundleConfigWebAssets()) : context.bundleConfigWebAssets())); + produceWebAssetsWithCheck(checkIfExists, context.bundleConfigWebAssets(), + webAssets -> bundleConfigAssets.produce(new BundleConfigAssetsBuildItem(webAssets))); - quteTagsAssets.produce(new QuteTagsBuildItem( - checkIfExists ? checkWebAssets(context.quteWebAssets()) : context.quteWebAssets())); + produceWebAssetsWithCheck(checkIfExists, context.quteWebAssets(), + webAssets -> quteTagsAssets.produce(new QuteTagsBuildItem(webAssets))); + } + private static void produceWebAssetsWithCheck(boolean checkIfExists, List e, + Consumer> consumer) { + final List webAssets = checkIfExists ? checkWebAssets(e) : e; + if (!webAssets.isEmpty()) { + consumer.accept(webAssets); + } } private static List checkWebAssets(List webAssets) { diff --git a/common-deployment/src/main/java/io/quarkiverse/web/bundler/deployment/HtmlTemplateAssetsScannerProcessor.java b/common-deployment/src/main/java/io/quarkiverse/web/bundler/deployment/QuteTemplateAssetsScannerProcessor.java similarity index 81% rename from common-deployment/src/main/java/io/quarkiverse/web/bundler/deployment/HtmlTemplateAssetsScannerProcessor.java rename to common-deployment/src/main/java/io/quarkiverse/web/bundler/deployment/QuteTemplateAssetsScannerProcessor.java index b7c48ac..6ed2f7b 100644 --- a/common-deployment/src/main/java/io/quarkiverse/web/bundler/deployment/HtmlTemplateAssetsScannerProcessor.java +++ b/common-deployment/src/main/java/io/quarkiverse/web/bundler/deployment/QuteTemplateAssetsScannerProcessor.java @@ -7,18 +7,18 @@ import org.jboss.logging.Logger; -import io.quarkiverse.web.bundler.deployment.items.HtmlTemplatesBuildItem; import io.quarkiverse.web.bundler.deployment.items.ProjectResourcesScannerBuildItem; +import io.quarkiverse.web.bundler.deployment.items.QuteTemplatesBuildItem; import io.quarkiverse.web.bundler.deployment.items.WebAsset; import io.quarkus.deployment.annotations.BuildStep; import io.quarkus.deployment.builditem.LiveReloadBuildItem; -public class HtmlTemplateAssetsScannerProcessor { +public class QuteTemplateAssetsScannerProcessor { - private static final Logger LOGGER = Logger.getLogger(HtmlTemplateAssetsScannerProcessor.class); + private static final Logger LOGGER = Logger.getLogger(QuteTemplateAssetsScannerProcessor.class); @BuildStep - HtmlTemplatesBuildItem scan(ProjectResourcesScannerBuildItem scanner, + QuteTemplatesBuildItem scan(ProjectResourcesScannerBuildItem scanner, WebBundlerConfig config, LiveReloadBuildItem liveReload) throws IOException { @@ -28,13 +28,13 @@ HtmlTemplatesBuildItem scan(ProjectResourcesScannerBuildItem scanner, && context != null && !hasChanged(config, liveReload, s -> s.substring(config.webRoot().length()).matches("^/.+\\.html$"))) { LOGGER.debug("Web bundler html templates scan not needed for live reload"); - return new HtmlTemplatesBuildItem(context.assets()); + return new QuteTemplatesBuildItem(context.assets()); } final List assets = scanner.scan(new ProjectResourcesScannerBuildItem.Scanner(config.webRoot(), "glob:*.html", config.charset())); liveReload.setContextObject(HtmlTemplatesContext.class, new HtmlTemplatesContext(assets)); LOGGER.debugf("Web bundler %d html templates found.", assets.size()); - return new HtmlTemplatesBuildItem(assets); + return new QuteTemplatesBuildItem(assets); } private record HtmlTemplatesContext(List assets) { diff --git a/common-deployment/src/main/java/io/quarkiverse/web/bundler/deployment/WebBundlerConfig.java b/common-deployment/src/main/java/io/quarkiverse/web/bundler/deployment/WebBundlerConfig.java index b7c8cf5..2f2675e 100644 --- a/common-deployment/src/main/java/io/quarkiverse/web/bundler/deployment/WebBundlerConfig.java +++ b/common-deployment/src/main/java/io/quarkiverse/web/bundler/deployment/WebBundlerConfig.java @@ -83,6 +83,14 @@ default String fromWebRoot(String dir) { */ WebDependenciesConfig dependencies(); + /** + * When enabled, Quarkus will create redirections from {bundlePath}/{entryPointKey}.{js,css} to the corresponding file + * containing the unique hash. + * This is useful for fixed external access to the bundle files (fullstack microservices). + */ + @WithDefault("false") + Boolean bundleRedirect(); + /** * The default charset */ @@ -117,10 +125,17 @@ interface WebDependenciesConfig { Optional nodeModules(); /** - * If enabled web dependencies will also be served, this is usually not needed as they are already bundled. + * Disable this option to allow using runtime web dependencies. + * When a runtime scope web dependency is used, the dependency will be present in the target app and served at runtime. + * When a compile only scope web dependency is used, the dependency will only be used at build time and will not be + * present in the target app. + * + * WARNING: Maven compile scope is considered as a runtime scope, use 'provided' for compile only. On Gradle, + * 'compileOnly' is compile only. + * */ - @WithDefault("false") - boolean serve(); + @WithDefault("true") + boolean compileOnly(); } diff --git a/common-deployment/src/main/java/io/quarkiverse/web/bundler/deployment/items/EntryPointBuildItem.java b/common-deployment/src/main/java/io/quarkiverse/web/bundler/deployment/items/EntryPointBuildItem.java index ffa5d9a..006b654 100644 --- a/common-deployment/src/main/java/io/quarkiverse/web/bundler/deployment/items/EntryPointBuildItem.java +++ b/common-deployment/src/main/java/io/quarkiverse/web/bundler/deployment/items/EntryPointBuildItem.java @@ -4,6 +4,7 @@ import java.util.List; import java.util.Objects; +import java.util.StringJoiner; import io.quarkus.builder.item.MultiBuildItem; @@ -40,4 +41,12 @@ public boolean equals(Object o) { public int hashCode() { return Objects.hash(entryPointKey, webAssets); } + + @Override + public String toString() { + return new StringJoiner(", ", EntryPointBuildItem.class.getSimpleName() + "[", "]") + .add("entryPointKey='" + entryPointKey + "'") + .add("webAssets=" + webAssets) + .toString(); + } } diff --git a/common-deployment/src/main/java/io/quarkiverse/web/bundler/deployment/items/HtmlTemplatesBuildItem.java b/common-deployment/src/main/java/io/quarkiverse/web/bundler/deployment/items/HtmlTemplatesBuildItem.java deleted file mode 100644 index a798577..0000000 --- a/common-deployment/src/main/java/io/quarkiverse/web/bundler/deployment/items/HtmlTemplatesBuildItem.java +++ /dev/null @@ -1,10 +0,0 @@ -package io.quarkiverse.web.bundler.deployment.items; - -import java.util.List; - -public final class HtmlTemplatesBuildItem extends WebAssetsBuildItem { - - public HtmlTemplatesBuildItem(List webAssets) { - super(webAssets); - } -} diff --git a/common-deployment/src/main/java/io/quarkiverse/web/bundler/deployment/items/QuteTemplatesBuildItem.java b/common-deployment/src/main/java/io/quarkiverse/web/bundler/deployment/items/QuteTemplatesBuildItem.java new file mode 100644 index 0000000..860c8b5 --- /dev/null +++ b/common-deployment/src/main/java/io/quarkiverse/web/bundler/deployment/items/QuteTemplatesBuildItem.java @@ -0,0 +1,10 @@ +package io.quarkiverse.web.bundler.deployment.items; + +import java.util.List; + +public final class QuteTemplatesBuildItem extends WebAssetsBuildItem { + + public QuteTemplatesBuildItem(List webAssets) { + super(webAssets); + } +} diff --git a/common-deployment/src/main/java/io/quarkiverse/web/bundler/deployment/staticresources/GeneratedStaticResourcesProcessor.java b/common-deployment/src/main/java/io/quarkiverse/web/bundler/deployment/staticresources/GeneratedStaticResourcesProcessor.java index 274fa6a..d74efde 100644 --- a/common-deployment/src/main/java/io/quarkiverse/web/bundler/deployment/staticresources/GeneratedStaticResourcesProcessor.java +++ b/common-deployment/src/main/java/io/quarkiverse/web/bundler/deployment/staticresources/GeneratedStaticResourcesProcessor.java @@ -4,7 +4,6 @@ import static io.quarkiverse.web.bundler.deployment.staticresources.GeneratedStaticResourceBuildItem.WatchMode.RESTART; import static io.quarkiverse.web.bundler.deployment.util.PathUtils.prefixWithSlash; -import java.io.File; import java.io.IOException; import java.io.UncheckedIOException; import java.nio.file.Files; @@ -13,6 +12,7 @@ import java.util.HashSet; import java.util.List; import java.util.Set; +import java.util.stream.Collectors; import org.jboss.logging.Logger; @@ -26,6 +26,7 @@ import io.quarkus.deployment.builditem.LiveReloadBuildItem; import io.quarkus.deployment.builditem.nativeimage.NativeImageResourceBuildItem; import io.quarkus.deployment.pkg.builditem.CurateOutcomeBuildItem; +import io.quarkus.deployment.pkg.builditem.OutputTargetBuildItem; import io.quarkus.vertx.http.deployment.spi.AdditionalStaticResourceBuildItem; public class GeneratedStaticResourcesProcessor { @@ -39,9 +40,14 @@ public void processStaticFiles( BuildProducer vertxStaticResourcesProducer, BuildProducer watchedFiles, CurateOutcomeBuildItem curateOutcome, + OutputTargetBuildItem outputTarget, LiveReloadBuildItem liveReload, LaunchModeBuildItem launchModeBuildItem) { - final File buildDir = launchModeBuildItem.getLaunchMode().isDevOrTest() ? getBuildDirectory(curateOutcome) : null; + if (staticResources.isEmpty()) { + return; + } + final Path buildDir = launchModeBuildItem.getLaunchMode().isDevOrTest() ? getBuildDirectory(outputTarget, curateOutcome) + : null; final StaticResourcesDevContext staticResourcesDevContext = liveReload .getContextObject(StaticResourcesDevContext.class); if (liveReload.isLiveReload() && staticResourcesDevContext != null) { @@ -51,7 +57,7 @@ public void processStaticFiles( // a build tool clean might be necessary to make sure the static resources are clean if (staticResources.stream().map(GeneratedStaticResourceBuildItem::getResourceName).noneMatch(r::equals)) { try { - Files.deleteIfExists(buildDir.toPath().resolve(r)); + Files.deleteIfExists(buildDir.resolve(r)); } catch (IOException e) { throw new RuntimeException(e); } @@ -81,7 +87,7 @@ public void processStaticFiles( .produce(new AdditionalStaticResourceBuildItem(prefixWithSlash(staticResource.getPublicPath()), false)); // for dev/test mode if (launchModeBuildItem.getLaunchMode().isDevOrTest()) { - Path targetPath = buildDir.toPath().resolve(staticResource.getResourceName()); + Path targetPath = buildDir.resolve(staticResource.getResourceName()); // TODO: Change detection could also be done automatically by comparing the content (might be slow) or a hash of it using dev context if (!Files.exists(targetPath) || staticResource.isChanged()) { try { @@ -98,32 +104,31 @@ public void processStaticFiles( liveReload.setContextObject(StaticResourcesDevContext.class, new StaticResourcesDevContext(generatedStaticFiles)); } - public static File getBuildDirectory(CurateOutcomeBuildItem curateOutcomeBuildItem) { - File buildDir = null; + public static Path getBuildDirectory(OutputTargetBuildItem outputTarget, CurateOutcomeBuildItem curateOutcomeBuildItem) { + if (Files.exists(outputTarget.getOutputDirectory().resolve("classes/META-INF/resources"))) { + return outputTarget.getOutputDirectory().resolve("classes"); + } + if (Files.exists(outputTarget.getOutputDirectory().resolve("resources/main/META-INF/resources"))) { + return outputTarget.getOutputDirectory().resolve("resources/main"); + } + Path buildDir = null; ArtifactSources src = curateOutcomeBuildItem.getApplicationModel().getAppArtifact().getSources(); if (src != null) { // shouldn't be null in dev mode - Collection srcDirs = src.getResourceDirs(); - if (srcDirs.isEmpty()) { + Collection dirs = src.getResourceDirs(); + if (dirs.isEmpty()) { // in the module has no resources dir? - srcDirs = src.getSourceDirs(); + dirs = src.getSourceDirs(); } - if (!srcDirs.isEmpty()) { + if (!dirs.isEmpty()) { + final Set outputDirs = dirs.stream().map(SourceDir::getOutputDir).collect(Collectors.toSet()); // pick the first resources output dir - Path resourcesOutputDir = srcDirs.iterator().next().getOutputDir(); - buildDir = resourcesOutputDir.toFile(); - if (srcDirs.size() > 1) { + buildDir = outputDirs.iterator().next(); + if (outputDirs.size() > 1) { LOGGER.warnf("Multiple resources directories found, using the first one in the list: %s", - resourcesOutputDir); + outputDirs); } - } } - if (buildDir == null) { - // the module doesn't have any sources nor resources, stick to the build dir - buildDir = new File( - curateOutcomeBuildItem.getApplicationModel().getAppArtifact().getWorkspaceModule().getBuildDir(), - "classes"); - } return buildDir; } diff --git a/deployment/pom.xml b/deployment/pom.xml index 37228d9..478f91a 100644 --- a/deployment/pom.xml +++ b/deployment/pom.xml @@ -23,17 +23,6 @@ quarkus-web-bundler-sass-compiler ${project.version} - - com.yahoo.platform.yui - yuicompressor - ${yuicompressor.version} - - io.quarkus.qute qute-core @@ -53,6 +42,11 @@ quarkus-junit5-internal test + + io.rest-assured + rest-assured + test + diff --git a/deployment/src/main/java/io/quarkiverse/web/bundler/deployment/HtmlTemplateWebAssetsProcessor.java b/deployment/src/main/java/io/quarkiverse/web/bundler/deployment/QuteTemplateWebAssetsProcessor.java similarity index 86% rename from deployment/src/main/java/io/quarkiverse/web/bundler/deployment/HtmlTemplateWebAssetsProcessor.java rename to deployment/src/main/java/io/quarkiverse/web/bundler/deployment/QuteTemplateWebAssetsProcessor.java index 9b69b4d..14322a0 100644 --- a/deployment/src/main/java/io/quarkiverse/web/bundler/deployment/HtmlTemplateWebAssetsProcessor.java +++ b/deployment/src/main/java/io/quarkiverse/web/bundler/deployment/QuteTemplateWebAssetsProcessor.java @@ -5,10 +5,9 @@ import static java.util.concurrent.CompletableFuture.completedFuture; import java.io.IOException; -import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; -import java.io.UncheckedIOException; +import java.net.URL; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.nio.file.Path; @@ -22,7 +21,7 @@ import org.eclipse.microprofile.config.ConfigProvider; import io.quarkiverse.web.bundler.deployment.items.GeneratedBundleBuildItem; -import io.quarkiverse.web.bundler.deployment.items.HtmlTemplatesBuildItem; +import io.quarkiverse.web.bundler.deployment.items.QuteTemplatesBuildItem; import io.quarkiverse.web.bundler.deployment.items.WebAsset; import io.quarkiverse.web.bundler.deployment.staticresources.GeneratedStaticResourceBuildItem; import io.quarkiverse.web.bundler.runtime.Bundle; @@ -32,10 +31,10 @@ import io.quarkus.deployment.builditem.LiveReloadBuildItem; import io.quarkus.qute.*; -public class HtmlTemplateWebAssetsProcessor { +public class QuteTemplateWebAssetsProcessor { @BuildStep void processHtmlTemplateWebAssets(WebBundlerConfig config, - HtmlTemplatesBuildItem htmlTemplates, + QuteTemplatesBuildItem htmlTemplates, GeneratedBundleBuildItem generatedBundle, BuildProducer staticResourceProducer, LiveReloadBuildItem liveReload, @@ -104,24 +103,25 @@ public Optional locate(String id) { return Optional.empty(); } String name = id.replace("web-bundler/", ""); - try (InputStream templateStream = this.getClass().getResourceAsStream("/templates/tags/" + name)) { - if (templateStream == null) { - return Optional.empty(); - } - return Optional.of(new TemplateLocation() { - @Override - public Reader read() { - return new InputStreamReader(templateStream, StandardCharsets.UTF_8); + 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(); - } - }); - } catch (IOException e) { - throw new UncheckedIOException(e); - } + @Override + public Optional getVariant() { + return Optional.empty(); + } + }); } } diff --git a/deployment/src/main/java/io/quarkiverse/web/bundler/deployment/WebBundlerProcessor.java b/deployment/src/main/java/io/quarkiverse/web/bundler/deployment/WebBundlerProcessor.java index 7d95421..87f8bc3 100644 --- a/deployment/src/main/java/io/quarkiverse/web/bundler/deployment/WebBundlerProcessor.java +++ b/deployment/src/main/java/io/quarkiverse/web/bundler/deployment/WebBundlerProcessor.java @@ -2,8 +2,7 @@ import static io.quarkiverse.web.bundler.deployment.StaticWebAssetsProcessor.makePublic; import static io.quarkiverse.web.bundler.deployment.items.BundleWebAsset.BundleType.MANUAL; -import static io.quarkiverse.web.bundler.deployment.util.PathUtils.join; -import static io.quarkiverse.web.bundler.deployment.util.PathUtils.surroundWithSlashes; +import static io.quarkiverse.web.bundler.deployment.util.PathUtils.*; import static io.quarkus.deployment.annotations.ExecutionTime.STATIC_INIT; import static java.util.Map.entry; import static java.util.Objects.requireNonNull; @@ -47,8 +46,8 @@ import io.quarkiverse.web.bundler.deployment.staticresources.GeneratedStaticResourceBuildItem; import io.quarkiverse.web.bundler.deployment.staticresources.GeneratedStaticResourceBuildItem.WatchMode; import io.quarkiverse.web.bundler.runtime.Bundle; +import io.quarkiverse.web.bundler.runtime.BundleRedirectHandlerRecorder; import io.quarkiverse.web.bundler.runtime.WebBundlerBuildRecorder; -import io.quarkiverse.web.bundler.runtime.WebDependenciesBlockerRecorder; import io.quarkiverse.web.bundler.sass.SassBuildTimeCompiler; import io.quarkus.arc.deployment.AdditionalBeanBuildItem; import io.quarkus.arc.deployment.SyntheticBeanBuildItem; @@ -99,7 +98,7 @@ class WebBundlerProcessor { void bundle(WebBundlerConfig config, WebDependenciesBuildItem webDependencies, List entryPoints, - BundleConfigAssetsBuildItem bundleConfigs, + Optional bundleConfig, BuildProducer staticResourceProducer, BuildProducer generatedBundleProducer, LiveReloadBuildItem liveReload, @@ -137,8 +136,8 @@ void bundle(WebBundlerConfig config, Files.createDirectories(targetDir); LOGGER.debugf("Preparing bundle in %s", targetDir); - if (!bundleConfigs.getWebAssets().isEmpty()) { - for (WebAsset webAsset : bundleConfigs.getWebAssets()) { + if (bundleConfig.isPresent()) { + for (WebAsset webAsset : bundleConfig.get().getWebAssets()) { if (webAsset.filePath().isPresent()) { final Path targetConfig = targetDir.resolve(webAsset.pathFromWebRoot(config.webRoot())); Files.deleteIfExists(targetConfig); @@ -369,13 +368,12 @@ void initBundleBean( @BuildStep @Record(STATIC_INIT) - void webDepBlocker(WebBundlerConfig config, BuildProducer routes, WebDependenciesBlockerRecorder recorder) { - if (!config.dependencies().serve()) { - routes.produce(RouteBuildItem.builder().orderedRoute("/_static/*", 0) - .handler(recorder.handler()) - .build()); - routes.produce(RouteBuildItem.builder().orderedRoute("/webjars/*", 0) - .handler(recorder.handler()) + void initBundleRedirect(WebBundlerConfig config, BuildProducer routes, + BundleRedirectHandlerRecorder recorder, GeneratedBundleBuildItem generatedBundle) { + if (config.bundleRedirect()) { + final Map bundle = generatedBundle != null ? generatedBundle.getBundle() : Map.of(); + routes.produce(RouteBuildItem.builder().route(join(prefixWithSlash(config.bundlePath()), "*")) + .handler(recorder.handler(bundle)) .build()); } } diff --git a/deployment/src/main/java/io/quarkiverse/web/bundler/deployment/WebDependenciesScannerProcessor.java b/deployment/src/main/java/io/quarkiverse/web/bundler/deployment/WebDependenciesScannerProcessor.java index a2ce20c..3f52fcc 100644 --- a/deployment/src/main/java/io/quarkiverse/web/bundler/deployment/WebDependenciesScannerProcessor.java +++ b/deployment/src/main/java/io/quarkiverse/web/bundler/deployment/WebDependenciesScannerProcessor.java @@ -11,30 +11,49 @@ import io.mvnpm.esbuild.model.WebDependency; import io.mvnpm.esbuild.model.WebDependency.WebDependencyType; +import io.quarkiverse.web.bundler.deployment.items.EntryPointBuildItem; import io.quarkiverse.web.bundler.deployment.items.WebDependenciesBuildItem; import io.quarkus.deployment.annotations.BuildStep; +import io.quarkus.deployment.builditem.LaunchModeBuildItem; import io.quarkus.deployment.pkg.builditem.CurateOutcomeBuildItem; import io.quarkus.maven.dependency.Dependency; import io.quarkus.maven.dependency.DependencyFlags; import io.quarkus.maven.dependency.ResolvedDependency; +import io.quarkus.runtime.configuration.ConfigurationException; class WebDependenciesScannerProcessor { private static final Logger LOGGER = Logger.getLogger(WebDependenciesScannerProcessor.class); @BuildStep - WebDependenciesBuildItem collectDependencies(CurateOutcomeBuildItem curateOutcome, WebBundlerConfig config) { + WebDependenciesBuildItem collectDependencies(LaunchModeBuildItem launchMode, + CurateOutcomeBuildItem curateOutcome, + WebBundlerConfig config, + List entryPoints) { + if (entryPoints.isEmpty()) { + return new WebDependenciesBuildItem(List.of()); + } final var stream = StreamSupport.stream(curateOutcome.getApplicationModel() - .getDependencies(DependencyFlags.COMPILE_ONLY | DependencyFlags.RUNTIME_CP).spliterator(), false); + .getDependenciesWithAnyFlag(DependencyFlags.COMPILE_ONLY, DependencyFlags.RUNTIME_CP).spliterator(), false); List webDeps = stream .filter(Dependency::isJar) .filter(d -> WebDependencyType.anyMatch(d.toCompactCoords())) + .peek(d -> checkScope(launchMode, d, config)) .map(WebDependenciesScannerProcessor::toWebDep) .filter(Objects::nonNull) .collect(Collectors.toList()); return new WebDependenciesBuildItem(webDeps); } + private void checkScope(LaunchModeBuildItem launchMode, ResolvedDependency d, WebBundlerConfig config) { + if (!launchMode.isTest() && config.dependencies().compileOnly() && d.isRuntimeCp()) { + throw new ConfigurationException( + ("The Web Bundler is configured to only include compileOnly web dependencies, but %s is set as runtime." + + " Use a compile only scope (e.g. provided) or set quarkus.web-bundler.dependencies.compile-only=false to allow runtime web dependencies.") + .formatted(d.toCompactCoords())); + } + } + private static WebDependency toWebDep(ResolvedDependency d) { return d.getResolvedPaths().stream().filter(p -> p.getFileName().toString().endsWith(".jar")).findFirst() .map(j -> new WebDependency(d.toCompactCoords(), j, resolveType(d.toCompactCoords()).orElseThrow())) diff --git a/deployment/src/test/java/io/quarkiverse/web/bundler/deployment/util/ConfiguredPathsTest.java b/deployment/src/test/java/io/quarkiverse/web/bundler/deployment/util/ConfiguredPathsTest.java deleted file mode 100644 index 6b62b2e..0000000 --- a/deployment/src/test/java/io/quarkiverse/web/bundler/deployment/util/ConfiguredPathsTest.java +++ /dev/null @@ -1,30 +0,0 @@ -package io.quarkiverse.web.bundler.deployment.util; - -import static io.quarkiverse.web.bundler.deployment.util.PathUtils.addTrailingSlash; -import static io.quarkiverse.web.bundler.deployment.util.PathUtils.join; -import static io.quarkiverse.web.bundler.deployment.util.PathUtils.prefixWithSlash; -import static io.quarkiverse.web.bundler.deployment.util.PathUtils.removeLeadingSlash; -import static io.quarkiverse.web.bundler.deployment.util.PathUtils.removeTrailingSlash; -import static org.junit.jupiter.api.Assertions.assertEquals; - -import org.junit.jupiter.api.Test; - -class ConfiguredPathsTest { - - @Test - void test() { - assertEquals("/hello", prefixWithSlash("hello")); - assertEquals("/hello", prefixWithSlash("/hello")); - assertEquals("/hello", removeTrailingSlash("/hello/")); - assertEquals("/hello", prefixWithSlash("/hello")); - assertEquals("hello", removeLeadingSlash("/hello")); - assertEquals("hello", removeLeadingSlash("hello")); - assertEquals("hello/", addTrailingSlash("hello")); - assertEquals("hello/", addTrailingSlash("hello/")); - assertEquals("hello/foo", join("hello/", "foo")); - assertEquals("hello/foo/", join("hello/", "foo/")); - assertEquals("http://hello/foo/", join("http://hello", "/foo/")); - assertEquals("http://hello/foo/", join("http://hello", "foo/")); - } - -} diff --git a/deployment/src/test/java/io/quarkiverse/web/bundler/test/WebBundlerDevModeTest.java b/deployment/src/test/java/io/quarkiverse/web/bundler/test/WebBundlerDevModeTest.java index 7c9d57b..608e435 100644 --- a/deployment/src/test/java/io/quarkiverse/web/bundler/test/WebBundlerDevModeTest.java +++ b/deployment/src/test/java/io/quarkiverse/web/bundler/test/WebBundlerDevModeTest.java @@ -1,25 +1,57 @@ package io.quarkiverse.web.bundler.test; +import org.hamcrest.Matchers; import org.jboss.shrinkwrap.api.ShrinkWrap; import org.jboss.shrinkwrap.api.spec.JavaArchive; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; import io.quarkus.test.QuarkusDevModeTest; +import io.restassured.RestAssured; -@Disabled public class WebBundlerDevModeTest { // Start hot reload (DevMode) test with your extension loaded + @RegisterExtension - static final QuarkusDevModeTest devModeTest = new QuarkusDevModeTest() - .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)); + static final QuarkusDevModeTest test = new QuarkusDevModeTest() + .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class) + .addAsResource("dev", "web") + // For some reason it fails without this + .addAsResource("META-INF/resources/test.txt") + .addAsResource("application.properties")); @Test - public void writeYourOwnDevModeTest() { - // Write your dev mode tests here - see the testing extension guide https://quarkus.io/guides/writing-extensions#testing-hot-reload for more information - Assertions.assertTrue(true, "Add dev mode assertions to " + getClass().getName()); + public void test() { + RestAssured.given() + .get("/") + .then() + .statusCode(200) + .body(Matchers.containsString("Hello Qute Static!")); + test.modifyResourceFile("web/index.html", s -> s.replace("Hello Qute Static!", "Hello Qute Static! Modified!")); + RestAssured.given() + .get("/") + .then() + .statusCode(200) + .body(Matchers.containsString("Hello Qute Static! Modified!")); + RestAssured.given() + .get("/static/bundle/main.js") + .then() + .statusCode(200) + .body(Matchers.containsString("console.log(\"Hello World!\");")); + test.modifyResourceFile("web/app/app.js", s -> s.replace("Hello World!", "Hello World! Modified!")); + RestAssured.given() + .get("/static/bundle/main.js") + .then() + .statusCode(200) + .body(Matchers.containsString("console.log(\"Hello World! Modified!\");")); + test.modifyResourceFile("web/app/app.css", s -> s.replace("background-color: #6b6bf5;", "background-color: red;")); + test.modifyResourceFile("web/app/other.scss", s -> s.replace("color: white;", "color: green;")); + RestAssured.given() + .get("/static/bundle/main.css") + .then() + .statusCode(200) + .body(Matchers.containsString("background-color: red;")) + .body(Matchers.containsString("color: green;")); } } diff --git a/deployment/src/test/java/io/quarkiverse/web/bundler/test/WebBundlerTest.java b/deployment/src/test/java/io/quarkiverse/web/bundler/test/WebBundlerTest.java index ec37817..55a4480 100644 --- a/deployment/src/test/java/io/quarkiverse/web/bundler/test/WebBundlerTest.java +++ b/deployment/src/test/java/io/quarkiverse/web/bundler/test/WebBundlerTest.java @@ -1,24 +1,56 @@ package io.quarkiverse.web.bundler.test; +import java.util.List; + +import jakarta.inject.Inject; + +import org.hamcrest.Matchers; import org.jboss.shrinkwrap.api.ShrinkWrap; import org.jboss.shrinkwrap.api.spec.JavaArchive; -import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; +import io.quarkiverse.web.bundler.runtime.Bundle; +import io.quarkus.maven.dependency.ArtifactDependency; import io.quarkus.test.QuarkusUnitTest; +import io.restassured.RestAssured; public class WebBundlerTest { - // Start unit test with your extension loaded @RegisterExtension static final QuarkusUnitTest unitTest = new QuarkusUnitTest() - .withEmptyApplication() - .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)); + .setForcedDependencies( + List.of(new ArtifactDependency("org.mvnpm", "jquery", null, "jar", "3.7.0", "provided", false))) + .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class) + .addAsResource("web") + // For some reason it fails without this + .addAsResource("META-INF/resources/test.txt") + .addAsResource("application.properties")); + + @Inject + Bundle bundle; @Test - public void writeYourOwnUnitTest() { - // Write your unit tests here - see the testing extension guide https://quarkus.io/guides/writing-extensions#testing-extensions for more information - Assertions.assertTrue(true, "Add some assertions to " + getClass().getName()); + public void test() { + RestAssured.given() + .get("/") + .then() + .statusCode(200) + .body(Matchers.containsString("mode:TEST")) + .body(Matchers.containsString("")) + .body(Matchers.containsString(" ")); + RestAssured.given() + .get(bundle.style("main")) + .then() + .statusCode(200); + RestAssured.given() + .get(bundle.script("main")) + .then() + .statusCode(200); + RestAssured.given() + .get("/static/hello.txt") + .then() + .statusCode(200) + .body(Matchers.equalTo("Hello World!")); } } diff --git a/deployment/src/test/resources/META-INF/resources/test.txt b/deployment/src/test/resources/META-INF/resources/test.txt new file mode 100644 index 0000000..e69de29 diff --git a/deployment/src/test/resources/application.properties b/deployment/src/test/resources/application.properties new file mode 100644 index 0000000..d880997 --- /dev/null +++ b/deployment/src/test/resources/application.properties @@ -0,0 +1,2 @@ +quarkus.web-bundler.dependencies.compile-only=false +quarkus.web-bundler.bundle-redirect=true \ No newline at end of file diff --git a/deployment/src/test/resources/dev/app/app.css b/deployment/src/test/resources/dev/app/app.css new file mode 100644 index 0000000..e8a43b1 --- /dev/null +++ b/deployment/src/test/resources/dev/app/app.css @@ -0,0 +1 @@ +body { background-color: #6b6bf5; } \ No newline at end of file diff --git a/deployment/src/test/resources/dev/app/app.js b/deployment/src/test/resources/dev/app/app.js new file mode 100644 index 0000000..de88829 --- /dev/null +++ b/deployment/src/test/resources/dev/app/app.js @@ -0,0 +1 @@ +console.log("Hello World!"); \ No newline at end of file diff --git a/deployment/src/test/resources/dev/app/other.scss b/deployment/src/test/resources/dev/app/other.scss new file mode 100644 index 0000000..30b9676 --- /dev/null +++ b/deployment/src/test/resources/dev/app/other.scss @@ -0,0 +1 @@ +p { color: white; } \ No newline at end of file diff --git a/deployment/src/test/resources/dev/index.html b/deployment/src/test/resources/dev/index.html new file mode 100644 index 0000000..e0121f7 --- /dev/null +++ b/deployment/src/test/resources/dev/index.html @@ -0,0 +1 @@ +Hello Qute Static! \ No newline at end of file diff --git a/deployment/src/test/resources/dev/static/hello.txt b/deployment/src/test/resources/dev/static/hello.txt new file mode 100644 index 0000000..c57eff5 --- /dev/null +++ b/deployment/src/test/resources/dev/static/hello.txt @@ -0,0 +1 @@ +Hello World! \ No newline at end of file diff --git a/deployment/src/test/resources/web/app/app.css b/deployment/src/test/resources/web/app/app.css new file mode 100644 index 0000000..e8a43b1 --- /dev/null +++ b/deployment/src/test/resources/web/app/app.css @@ -0,0 +1 @@ +body { background-color: #6b6bf5; } \ No newline at end of file diff --git a/deployment/src/test/resources/web/app/app.js b/deployment/src/test/resources/web/app/app.js new file mode 100644 index 0000000..a092ea7 --- /dev/null +++ b/deployment/src/test/resources/web/app/app.js @@ -0,0 +1,5 @@ +import $ from 'jquery'; + +$(document).ready(function() { + console.log("hello world"); +}); diff --git a/deployment/src/test/resources/web/app/other.scss b/deployment/src/test/resources/web/app/other.scss new file mode 100644 index 0000000..30b9676 --- /dev/null +++ b/deployment/src/test/resources/web/app/other.scss @@ -0,0 +1 @@ +p { color: white; } \ No newline at end of file diff --git a/deployment/src/test/resources/web/index.html b/deployment/src/test/resources/web/index.html new file mode 100644 index 0000000..c3214c1 --- /dev/null +++ b/deployment/src/test/resources/web/index.html @@ -0,0 +1,3 @@ +mode:{build:launchMode} +script:{#bundle tag="script"/} +style:{#bundle tag="style"/} \ No newline at end of file diff --git a/deployment/src/test/resources/web/static/hello.txt b/deployment/src/test/resources/web/static/hello.txt new file mode 100644 index 0000000..c57eff5 --- /dev/null +++ b/deployment/src/test/resources/web/static/hello.txt @@ -0,0 +1 @@ +Hello World! \ No newline at end of file diff --git a/docs/modules/ROOT/pages/advanced-guides.adoc b/docs/modules/ROOT/pages/advanced-guides.adoc index 23023c9..af221e7 100644 --- a/docs/modules/ROOT/pages/advanced-guides.adoc +++ b/docs/modules/ROOT/pages/advanced-guides.adoc @@ -156,8 +156,14 @@ NOTE: You may create different qute components groups to be used in different pa The Web Bundler is integrated with NPM dependencies through <> (default) or <>. Once added in the pom.xml the dependencies are directly available through import from the scripts and styles. +Using the Web Bundler, Web Dependencies are bundled, there is not point for the jars to be packaged in the resulting app. +Web Dependencies with `provided` scope (or `compileOnly` with Gradle) will not be packaged in the resulting app. + +INFO: By default, the Web Bundler will fail at build time if it detects non compile only Web Dependencies. You can configure xref:config-reference.adoc#quarkus-web-bundler_quarkus.web-bundler.dependencies.compile-only[a flag] to allow them but keep in mind that they will be served by Quarkus. + WARNING: If you don't import a Web Dependency from an entry-point (<>), it won't be bundled (dead code elimination). + [#mvnpm] === MVNPM (default) @@ -175,6 +181,7 @@ Lookup for packages on https://mvnpm.org or https://www.npmjs.com/ then add them org.mvnpm // <1> jquery // <2> 3.7.0 // <3> + provided // <4> ... @@ -183,6 +190,7 @@ Lookup for packages on https://mvnpm.org or https://www.npmjs.com/ then add them <1> use `org.mvnpm` or `org.mvnpm.at.something` for `@something/dep` <2> All dependencies published on NPM are available <3> Any https://www.npmjs.com/package/jquery?activeTab=versions[published NPM version] for your dependency +<4> Use `provided` scope to avoid having the dependency packaged in the target application If a package or a version in not yet available in Maven Central: @@ -273,6 +281,7 @@ quarkus.web-bundler.dependencies.type=webjars org.webjars.npm jquery 3.7.0 + provided ---- diff --git a/docs/modules/ROOT/pages/includes/quarkus-web-bundler.adoc b/docs/modules/ROOT/pages/includes/quarkus-web-bundler.adoc index 9289cf6..770b1b7 100644 --- a/docs/modules/ROOT/pages/includes/quarkus-web-bundler.adoc +++ b/docs/modules/ROOT/pages/includes/quarkus-web-bundler.adoc @@ -367,18 +367,35 @@ endif::add-copy-button-to-env-var[] |`node_modules will be in the build/target directory` -a|icon:lock[title=Fixed at build time] [[quarkus-web-bundler_quarkus.web-bundler.dependencies.serve]]`link:#quarkus-web-bundler_quarkus.web-bundler.dependencies.serve[quarkus.web-bundler.dependencies.serve]` +a|icon:lock[title=Fixed at build time] [[quarkus-web-bundler_quarkus.web-bundler.dependencies.compile-only]]`link:#quarkus-web-bundler_quarkus.web-bundler.dependencies.compile-only[quarkus.web-bundler.dependencies.compile-only]` [.description] -- -If enabled web dependencies will also be served, this is usually not needed as they are already bundled. +Disable this option to allow using runtime web dependencies. When a runtime scope web dependency is used, the dependency will be present in the target app and served at runtime. When a compile only scope web dependency is used, the dependency will only be used at build time and will not be present in the target app. WARNING: Maven compile scope is considered as a runtime scope, use 'provided' for compile only. On Gradle, 'compileOnly' is compile only. ifdef::add-copy-button-to-env-var[] -Environment variable: env_var_with_copy_button:+++QUARKUS_WEB_BUNDLER_DEPENDENCIES_SERVE+++[] +Environment variable: env_var_with_copy_button:+++QUARKUS_WEB_BUNDLER_DEPENDENCIES_COMPILE_ONLY+++[] endif::add-copy-button-to-env-var[] ifndef::add-copy-button-to-env-var[] -Environment variable: `+++QUARKUS_WEB_BUNDLER_DEPENDENCIES_SERVE+++` +Environment variable: `+++QUARKUS_WEB_BUNDLER_DEPENDENCIES_COMPILE_ONLY+++` +endif::add-copy-button-to-env-var[] +--|boolean +|`true` + + +a|icon:lock[title=Fixed at build time] [[quarkus-web-bundler_quarkus.web-bundler.bundle-redirect]]`link:#quarkus-web-bundler_quarkus.web-bundler.bundle-redirect[quarkus.web-bundler.bundle-redirect]` + + +[.description] +-- +When enabled, Quarkus will create redirections from ++{++bundlePath++}++/++{++entryPointKey++}++.++{++js,css++}++ to the corresponding file containing the unique hash. This is useful for fixed external access to the bundle files (fullstack microservices). + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_WEB_BUNDLER_BUNDLE_REDIRECT+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_WEB_BUNDLER_BUNDLE_REDIRECT+++` endif::add-copy-button-to-env-var[] --|boolean |`false` @@ -455,6 +472,7 @@ endif::add-copy-button-to-env-var[] a|icon:lock[title=Fixed at build time] [[quarkus-web-bundler_quarkus.web-bundler.bundle.-bundle-.qute-tags]]`link:#quarkus-web-bundler_quarkus.web-bundler.bundle.-bundle-.qute-tags[quarkus.web-bundler.bundle."bundle".qute-tags]` + [.description] -- Indicate if this directory contains qute tags (as .html files) This is only available if the Quarkus Qute extension is in the project. diff --git a/integration-tests/.locker/pom.xml b/integration-tests/.locker/pom.xml index 46c49c3..beebbf6 100644 --- a/integration-tests/.locker/pom.xml +++ b/integration-tests/.locker/pom.xml @@ -1,39 +1,44 @@ - + 4.0.0 - - io.quarkiverse.web-bundler + + io.quarkiverse.web-bundler + quarkus-web-bundler-parent + 999-SNAPSHOT + ../../pom.xml + quarkus-web-bundler-integration-tests-locker - 999-SNAPSHOT pom + org.mvnpm.at.hotwired stimulus 3.2.2 + provided org.mvnpm bootstrap 5.3.1 + provided org.mvnpm jquery 3.7.0 + provided org.mvnpm slick-carousel 1.8.1 + provided - - sha512:MfcV4RStTxVwzwZb1gnSC74UABMGxYay8qDK9g2iDX1TqrIj5cCv2wm5VkWNE2MOaNLT6IXp8JDiVfUL529TmQ== - sha512:VITLgIqXKQFyWLPhprfQpfhmXGXSSF1JX12ySYXkvCQliN94QmNcRJUQPSAjVahmWNTyVmutxtKRZNKqiK0OPg== - sha512:6spsOuarJ4OlG4MJGN3rKS6XooPkgWpyjl9T9j35xJrS1ECjfnx6CG1UbURXCaqfGJ9ZcbO3G5VxXFfSsVcvVg== - sha512:kaObqoqxbGvkbSY7gz2AFVWRjirse/RT8Sa5w56MtUgVtWqGjLPwXizy1s3rSvMzvtkiOUWNjuTLE8/rTfFV6g== - diff --git a/integration-tests/pom.xml b/integration-tests/pom.xml index 1051c35..8481359 100644 --- a/integration-tests/pom.xml +++ b/integration-tests/pom.xml @@ -40,13 +40,14 @@ org.mvnpm bootstrap - runtime + provided 5.3.1 org.mvnpm.at.hotwired stimulus 3.2.2 + provided io.quarkus @@ -129,7 +130,9 @@ locker - true + + !unlocked + diff --git a/pom.xml b/pom.xml index 810fbac..bf18320 100644 --- a/pom.xml +++ b/pom.xml @@ -30,7 +30,7 @@ 17 UTF-8 UTF-8 - 999-SNAPSHOT + 3.7.0 1.1.2 4.1.1 diff --git a/qute-components/deployment/src/main/java/io/quarkiverse/web/bundler/qute/components/deployment/WebBundlerQuteComponentsProcessor.java b/qute-components/deployment/src/main/java/io/quarkiverse/web/bundler/qute/components/deployment/WebBundlerQuteComponentsProcessor.java index 56bebfa..fd26565 100644 --- a/qute-components/deployment/src/main/java/io/quarkiverse/web/bundler/qute/components/deployment/WebBundlerQuteComponentsProcessor.java +++ b/qute-components/deployment/src/main/java/io/quarkiverse/web/bundler/qute/components/deployment/WebBundlerQuteComponentsProcessor.java @@ -3,10 +3,7 @@ import static io.quarkiverse.web.bundler.qute.components.runtime.WebBundlerQuteContextRecorder.WEB_BUNDLER_ID_PREFIX; import static io.quarkus.deployment.annotations.ExecutionTime.STATIC_INIT; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; import org.jboss.logging.Logger; @@ -30,10 +27,13 @@ void initQuteTags( BuildProducer additionalBeans, BuildProducer syntheticBeans, WebBundlerQuteContextRecorder recorder, - QuteTagsBuildItem quteTags) { + Optional quteTags) { + if (quteTags.isEmpty()) { + return; + } final Map templates = new HashMap<>(); final List tags = new ArrayList<>(); - for (WebAsset webAsset : quteTags.getWebAssets()) { + for (WebAsset webAsset : quteTags.get().getWebAssets()) { final String tag = webAsset.filePath().get().getFileName().toString(); final String tagName = tag.contains(".") ? tag.substring(0, tag.indexOf('.')) : tag; templates.put(WEB_BUNDLER_ID_PREFIX + tagName, new String(webAsset.readContentFromFile(), webAsset.charset())); diff --git a/runtime/src/main/java/io/quarkiverse/web/bundler/runtime/BundleRedirectHandlerRecorder.java b/runtime/src/main/java/io/quarkiverse/web/bundler/runtime/BundleRedirectHandlerRecorder.java new file mode 100644 index 0000000..702d071 --- /dev/null +++ b/runtime/src/main/java/io/quarkiverse/web/bundler/runtime/BundleRedirectHandlerRecorder.java @@ -0,0 +1,26 @@ +package io.quarkiverse.web.bundler.runtime; + +import java.util.Map; + +import io.quarkus.runtime.annotations.Recorder; +import io.vertx.core.Handler; +import io.vertx.ext.web.RoutingContext; + +@Recorder +public class BundleRedirectHandlerRecorder { + + public Handler handler(Map bundle) { + return event -> { + final String path = event.normalizedPath(); + final String entryPoint = path.substring(path.lastIndexOf('/') + 1); + if (!bundle.containsKey(entryPoint)) { + event.next(); + return; + } + event.response().setStatusCode(302); + event.response().putHeader("Cache-Control", "no-cache, no-store, must-revalidate"); + event.response().putHeader("Location", bundle.get(entryPoint)); + event.response().end(); + }; + } +} diff --git a/runtime/src/main/java/io/quarkiverse/web/bundler/runtime/WebDependenciesBlockerRecorder.java b/runtime/src/main/java/io/quarkiverse/web/bundler/runtime/WebDependenciesBlockerRecorder.java deleted file mode 100644 index 54b000b..0000000 --- a/runtime/src/main/java/io/quarkiverse/web/bundler/runtime/WebDependenciesBlockerRecorder.java +++ /dev/null @@ -1,16 +0,0 @@ -package io.quarkiverse.web.bundler.runtime; - -import io.quarkus.runtime.annotations.Recorder; -import io.vertx.core.Handler; -import io.vertx.ext.web.RoutingContext; - -@Recorder -public class WebDependenciesBlockerRecorder { - - public Handler handler() { - return event -> { - event.response().setStatusCode(404); - event.response().send(); - }; - } -} diff --git a/runtime/src/main/resources/application.properties b/runtime/src/main/resources/application.properties index e69de29..8a9a92d 100644 --- a/runtime/src/main/resources/application.properties +++ b/runtime/src/main/resources/application.properties @@ -0,0 +1 @@ +%dev.quarkus.http.static-resources.caching-enabled=false \ No newline at end of file