From b9488de230cd19630b2266bd0ebcfb9141a10d5d Mon Sep 17 00:00:00 2001 From: Andy Damevin Date: Wed, 6 Dec 2023 16:42:14 +0100 Subject: [PATCH] Various improvements in user xp --- README.md | 3 +- common-deployment/pom.xml | 42 ++++ .../deployment/ConfiguredEntryPoint.java | 0 .../deployment/ProjectResourcesScanner.java | 0 .../deployment/WebAssetsScannerProcessor.java | 52 +++-- .../bundler/deployment/WebBundlerConfig.java | 91 +------- .../items/BundleConfigAssetsBuildItem.java | 0 .../deployment/items/BundleWebAsset.java | 0 .../deployment/items/DefaultWebAsset.java | 0 .../deployment/items/EntryPointBuildItem.java | 0 .../items/HtmlTemplatesBuildItem.java | 10 + .../deployment/items/QuteTagsBuildItem.java | 0 .../items/StaticAssetsBuildItem.java | 0 .../items/StylesAssetsBuildItem.java | 0 .../bundler/deployment/items/WebAsset.java | 4 + .../deployment/items/WebAssetsBuildItem.java | 0 .../GeneratedStaticResourceBuildItem.java | 0 .../GeneratedStaticResourcesProcessor.java | 0 .../StaticResourcesDevContext.java | 0 .../bundler/deployment/util/PathUtils.java | 0 deployment/pom.xml | 18 +- .../ScriptDependenciesScannerProcessor.java | 36 ---- .../deployment/WebBundlerProcessor.java | 196 ++++++++++++++---- .../WebDependenciesScannerProcessor.java | 39 ++++ .../items/WebDependenciesBuildItem.java | 15 +- docs/modules/ROOT/pages/advanced-guides.adoc | 89 ++++---- .../pages/includes/quarkus-web-bundler.adoc | 89 ++------ docs/modules/ROOT/pages/index.adoc | 4 +- docs/modules/ROOT/pages/main-concepts.adoc | 4 +- .../src/main/resources/application.properties | 3 + .../src/main/resources/web/test.html | 26 +++ pom.xml | 4 +- qute-components/deployment/pom.xml | 52 +++++ .../WebBundlerQuteComponentsProcessor.java | 48 +++++ qute-components/pom.xml | 16 ++ qute-components/runtime/pom.xml | 60 ++++++ .../WebBundlerQuteContextRecorder.java | 2 +- .../WebBundlerQuteEngineObserver.java | 6 +- .../resources/META-INF/quarkus-extension.yaml | 12 ++ runtime/pom.xml | 8 +- 40 files changed, 593 insertions(+), 336 deletions(-) create mode 100644 common-deployment/pom.xml rename {deployment => common-deployment}/src/main/java/io/quarkiverse/web/bundler/deployment/ConfiguredEntryPoint.java (100%) rename {deployment => common-deployment}/src/main/java/io/quarkiverse/web/bundler/deployment/ProjectResourcesScanner.java (100%) rename {deployment => common-deployment}/src/main/java/io/quarkiverse/web/bundler/deployment/WebAssetsScannerProcessor.java (82%) rename {deployment => common-deployment}/src/main/java/io/quarkiverse/web/bundler/deployment/WebBundlerConfig.java (74%) rename {deployment => common-deployment}/src/main/java/io/quarkiverse/web/bundler/deployment/items/BundleConfigAssetsBuildItem.java (100%) rename {deployment => common-deployment}/src/main/java/io/quarkiverse/web/bundler/deployment/items/BundleWebAsset.java (100%) rename {deployment => common-deployment}/src/main/java/io/quarkiverse/web/bundler/deployment/items/DefaultWebAsset.java (100%) rename {deployment => common-deployment}/src/main/java/io/quarkiverse/web/bundler/deployment/items/EntryPointBuildItem.java (100%) create mode 100644 common-deployment/src/main/java/io/quarkiverse/web/bundler/deployment/items/HtmlTemplatesBuildItem.java rename {deployment => common-deployment}/src/main/java/io/quarkiverse/web/bundler/deployment/items/QuteTagsBuildItem.java (100%) rename {deployment => common-deployment}/src/main/java/io/quarkiverse/web/bundler/deployment/items/StaticAssetsBuildItem.java (100%) rename {deployment => common-deployment}/src/main/java/io/quarkiverse/web/bundler/deployment/items/StylesAssetsBuildItem.java (100%) rename {deployment => common-deployment}/src/main/java/io/quarkiverse/web/bundler/deployment/items/WebAsset.java (90%) rename {deployment => common-deployment}/src/main/java/io/quarkiverse/web/bundler/deployment/items/WebAssetsBuildItem.java (100%) rename {deployment => common-deployment}/src/main/java/io/quarkiverse/web/bundler/deployment/staticresources/GeneratedStaticResourceBuildItem.java (100%) rename {deployment => common-deployment}/src/main/java/io/quarkiverse/web/bundler/deployment/staticresources/GeneratedStaticResourcesProcessor.java (100%) rename {deployment => common-deployment}/src/main/java/io/quarkiverse/web/bundler/deployment/staticresources/StaticResourcesDevContext.java (100%) rename {deployment => common-deployment}/src/main/java/io/quarkiverse/web/bundler/deployment/util/PathUtils.java (100%) delete mode 100644 deployment/src/main/java/io/quarkiverse/web/bundler/deployment/ScriptDependenciesScannerProcessor.java create mode 100644 deployment/src/main/java/io/quarkiverse/web/bundler/deployment/WebDependenciesScannerProcessor.java create mode 100644 integration-tests/src/main/resources/web/test.html create mode 100644 qute-components/deployment/pom.xml create mode 100644 qute-components/deployment/src/main/java/io/quarkiverse/web/bundler/qute/components/deployment/WebBundlerQuteComponentsProcessor.java create mode 100644 qute-components/pom.xml create mode 100644 qute-components/runtime/pom.xml rename {runtime/src/main/java/io/quarkiverse/web/bundler/runtime/qute => qute-components/runtime/src/main/java/io/quarkiverse/web/bundler/qute/components/runtime}/WebBundlerQuteContextRecorder.java (94%) rename {runtime/src/main/java/io/quarkiverse/web/bundler/runtime/qute => qute-components/runtime/src/main/java/io/quarkiverse/web/bundler/qute/components/runtime}/WebBundlerQuteEngineObserver.java (86%) create mode 100644 qute-components/runtime/src/main/resources/META-INF/quarkus-extension.yaml diff --git a/README.md b/README.md index 0f4169e..5290199 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,8 @@ No need to install NodeJs, it relies on a Java wrapped version of [esbuild](http * [x] Production build * [x] Awesome Dev experience * [x] Integrated with NPM dependencies through [mvnpm](https://docs.quarkiverse.io/quarkus-web-bundler/dev/advanced-guides.html#mvnpm) or [webjars](https://docs.quarkiverse.io/quarkus-web-bundler/dev/advanced-guides.html#webjars). -* [x] Server Side Web Components (Qute template + Script + Style) +* [x] Build-time index.html rendering with bundled scripts and styles +* [x] Server Side Qute Components (Qute template + Script + Style) All the information you need to use Quarkus Web Bundler is in the [user documentation](https://docs.quarkiverse.io/quarkus-web-bundler/dev/). diff --git a/common-deployment/pom.xml b/common-deployment/pom.xml new file mode 100644 index 0000000..debce04 --- /dev/null +++ b/common-deployment/pom.xml @@ -0,0 +1,42 @@ + + + 4.0.0 + + io.quarkiverse.web-bundler + quarkus-web-bundler-parent + 999-SNAPSHOT + + quarkus-web-bundler-common-deployment + Quarkus Web Bundler - Common - Deployment + + + io.quarkus + quarkus-arc-deployment + + + io.quarkus + quarkus-vertx-http-deployment + + + io.quarkus + quarkus-junit5-internal + test + + + + + + maven-compiler-plugin + + + + io.quarkus + quarkus-extension-processor + ${quarkus.version} + + + + + + + diff --git a/deployment/src/main/java/io/quarkiverse/web/bundler/deployment/ConfiguredEntryPoint.java b/common-deployment/src/main/java/io/quarkiverse/web/bundler/deployment/ConfiguredEntryPoint.java similarity index 100% rename from deployment/src/main/java/io/quarkiverse/web/bundler/deployment/ConfiguredEntryPoint.java rename to common-deployment/src/main/java/io/quarkiverse/web/bundler/deployment/ConfiguredEntryPoint.java diff --git a/deployment/src/main/java/io/quarkiverse/web/bundler/deployment/ProjectResourcesScanner.java b/common-deployment/src/main/java/io/quarkiverse/web/bundler/deployment/ProjectResourcesScanner.java similarity index 100% rename from deployment/src/main/java/io/quarkiverse/web/bundler/deployment/ProjectResourcesScanner.java rename to common-deployment/src/main/java/io/quarkiverse/web/bundler/deployment/ProjectResourcesScanner.java diff --git a/deployment/src/main/java/io/quarkiverse/web/bundler/deployment/WebAssetsScannerProcessor.java b/common-deployment/src/main/java/io/quarkiverse/web/bundler/deployment/WebAssetsScannerProcessor.java similarity index 82% rename from deployment/src/main/java/io/quarkiverse/web/bundler/deployment/WebAssetsScannerProcessor.java rename to common-deployment/src/main/java/io/quarkiverse/web/bundler/deployment/WebAssetsScannerProcessor.java index 7d50848..a194267 100644 --- a/deployment/src/main/java/io/quarkiverse/web/bundler/deployment/WebAssetsScannerProcessor.java +++ b/common-deployment/src/main/java/io/quarkiverse/web/bundler/deployment/WebAssetsScannerProcessor.java @@ -20,6 +20,7 @@ import io.quarkiverse.web.bundler.deployment.items.BundleWebAsset; import io.quarkiverse.web.bundler.deployment.items.BundleWebAsset.BundleType; import io.quarkiverse.web.bundler.deployment.items.EntryPointBuildItem; +import io.quarkiverse.web.bundler.deployment.items.HtmlTemplatesBuildItem; import io.quarkiverse.web.bundler.deployment.items.QuteTagsBuildItem; import io.quarkiverse.web.bundler.deployment.items.StaticAssetsBuildItem; import io.quarkiverse.web.bundler.deployment.items.WebAsset; @@ -53,6 +54,7 @@ void collect(ApplicationArchivesBuildItem applicationArchives, BuildProducer staticAssets, BuildProducer quteTagsAssets, BuildProducer bundleConfigAssets, + BuildProducer htmlTemplatesAssets, BuildProducer watchedFiles, WebBundlerConfig config, LiveReloadBuildItem liveReload) @@ -67,7 +69,7 @@ void collect(ApplicationArchivesBuildItem applicationArchives, // Project WebAssets shouldn't be changed even if the file is changed as content is not stored // WebAsset from dependencies means we should do a new scan LOGGER.debug("Web bundler scan not needed for live reload"); - produceWebAssets(bundles, staticAssets, quteTagsAssets, bundleConfigAssets, devContext, true); + produceWebAssets(bundles, staticAssets, quteTagsAssets, bundleConfigAssets, htmlTemplatesAssets, devContext, true); return; } LOGGER.debug("Web bundler scan started"); @@ -76,23 +78,25 @@ void collect(ApplicationArchivesBuildItem applicationArchives, .filter(Dependency::isRuntimeExtensionArtifact).collect(Collectors.toList()); Map entryPointsConfig = new HashMap<>(config.bundle()); final List staticAssetsScanners = new ArrayList<>(); + final List htmlTemplateAssetsScanner = new ArrayList<>(); final List quteTagsAssetsScanners = new ArrayList<>(); final List bundleConfigAssetsScanners = new ArrayList<>(); - if (config.presets().components().enabled()) { - entryPointsConfig.put("components", - new ConfiguredEntryPoint("components", "components", - config.presets().components().entryPointKey().orElse( - MAIN_ENTRYPOINT_KEY))); - quteTagsAssetsScanners.add(new Scanner(config.fromWebRoot("components"), "glob:**.html", config.charset())); + if (!config.bundle().containsKey("app")) { + entryPointsConfig.put("app", new ConfiguredEntryPoint("app", "app", MAIN_ENTRYPOINT_KEY)); } + + final EntryPointConfig componentsEntryPoint = config.bundle().get("qute-components"); + if (componentsEntryPoint != null && componentsEntryPoint.enabled()) { + quteTagsAssetsScanners.add(new Scanner(config.fromWebRoot(componentsEntryPoint.effectiveDir("qute-components")), + "glob:**.html", config.charset())); + } + final ProjectResourcesScanner resourcesScanner = new ProjectResourcesScanner(allApplicationArchives, extensionArtifacts); - if (config.presets().app().enabled()) { - entryPointsConfig.put("app", - new ConfiguredEntryPoint("app", "app", - config.presets().app().entryPointKey().orElse(MAIN_ENTRYPOINT_KEY))); - } + + htmlTemplateAssetsScanner.add(new Scanner(config.webRoot(), + "glob:*.html", config.charset())); staticAssetsScanners.add(new Scanner(config.fromWebRoot(config.staticDir()), "glob:**", config.charset())); @@ -122,9 +126,11 @@ void collect(ApplicationArchivesBuildItem applicationArchives, final WebAssetsLookupDevContext context = new WebAssetsLookupDevContext( bundleAssets, - resourcesScanner.scan(staticAssetsScanners), resourcesScanner.scan(quteTagsAssetsScanners), - resourcesScanner.scan(bundleConfigAssetsScanners)); - produceWebAssets(bundles, staticAssets, quteTagsAssets, bundleConfigAssets, context, false); + resourcesScanner.scan(staticAssetsScanners), + resourcesScanner.scan(quteTagsAssetsScanners), + resourcesScanner.scan(bundleConfigAssetsScanners), + resourcesScanner.scan(htmlTemplateAssetsScanner)); + produceWebAssets(bundles, staticAssets, quteTagsAssets, bundleConfigAssets, htmlTemplatesAssets, context, false); liveReload.setContextObject(WebAssetsLookupDevContext.class, context); } @@ -139,9 +145,13 @@ private static boolean hasNewWebResources(WebBundlerConfig config, LiveReloadBui .allMatch(webAssets::contains); } - void produceWebAssets(BuildProducer bundles, BuildProducer staticAssets, - BuildProducer quteTagsAssets, BuildProducer bundleConfigAssets, - WebAssetsLookupDevContext context, boolean checkIfExists) { + void produceWebAssets(BuildProducer bundles, + BuildProducer staticAssets, + BuildProducer quteTagsAssets, + BuildProducer bundleConfigAssets, + BuildProducer htmlTemplatesAssets, + WebAssetsLookupDevContext context, + boolean checkIfExists) { for (Map.Entry> e : context.bundleAssets().entrySet()) { bundles.produce(new EntryPointBuildItem(e.getKey(), checkIfExists ? checkWebAssets(e.getValue()) : e.getValue())); } @@ -154,6 +164,9 @@ void produceWebAssets(BuildProducer bundles, BuildProducer< quteTagsAssets.produce(new QuteTagsBuildItem( checkIfExists ? checkWebAssets(context.quteWebAssets()) : context.quteWebAssets())); + htmlTemplatesAssets.produce(new HtmlTemplatesBuildItem( + checkIfExists ? checkWebAssets(context.htmlTemplateWebAssets()) : context.htmlTemplateWebAssets())); + } private static List checkWebAssets(List webAssets) { @@ -163,13 +176,14 @@ private static List checkWebAssets(List webAssets) { } record WebAssetsLookupDevContext(Map> bundleAssets, List staticWebAssets, - List quteWebAssets, List bundleConfigWebAssets) { + List quteWebAssets, List bundleConfigWebAssets, List htmlTemplateWebAssets) { public List allWebAssets() { final ArrayList all = new ArrayList<>(); all.addAll(staticWebAssets); all.addAll(quteWebAssets); all.addAll(bundleConfigWebAssets); + all.addAll(htmlTemplateWebAssets); bundleAssets.values().forEach(all::addAll); return all; } diff --git a/deployment/src/main/java/io/quarkiverse/web/bundler/deployment/WebBundlerConfig.java b/common-deployment/src/main/java/io/quarkiverse/web/bundler/deployment/WebBundlerConfig.java similarity index 74% rename from deployment/src/main/java/io/quarkiverse/web/bundler/deployment/WebBundlerConfig.java rename to common-deployment/src/main/java/io/quarkiverse/web/bundler/deployment/WebBundlerConfig.java index 112a738..1a9b004 100644 --- a/deployment/src/main/java/io/quarkiverse/web/bundler/deployment/WebBundlerConfig.java +++ b/common-deployment/src/main/java/io/quarkiverse/web/bundler/deployment/WebBundlerConfig.java @@ -1,9 +1,7 @@ package io.quarkiverse.web.bundler.deployment; -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 java.util.function.Predicate.not; import java.nio.charset.Charset; @@ -11,15 +9,10 @@ import java.util.Map; import java.util.Optional; import java.util.Set; -import java.util.function.Predicate; - -import jakarta.validation.constraints.NotBlank; -import jakarta.validation.constraints.Pattern; import org.eclipse.microprofile.config.Config; import org.eclipse.microprofile.config.ConfigProvider; -import io.quarkus.maven.dependency.Dependency; import io.quarkus.runtime.annotations.ConfigDocDefault; import io.quarkus.runtime.annotations.ConfigPhase; import io.quarkus.runtime.annotations.ConfigRoot; @@ -36,16 +29,16 @@ public interface WebBundlerConfig { * The directory in the resources which serves as root for the web assets */ @WithDefault("web") - @NotBlank String webRoot(); default String fromWebRoot(String dir) { - return addTrailingSlash(webRoot()) + removeLeadingSlash(dir); + return join(webRoot(), dir); } /** - * Bundles of scripts and styles + * Bundle entry points config for scripts and styles */ + @ConfigDocDefault("if not overridden, by default, 'app' directory will be bundled with 'main' as entry point key") Map bundle(); /** @@ -55,7 +48,6 @@ default String fromWebRoot(String dir) { */ @WithName("static") @WithDefault("static") - @Pattern(regexp = "") String staticDir(); /** @@ -67,11 +59,6 @@ default String fromWebRoot(String dir) { @WithDefault("static/bundle") String bundlePath(); - /** - * The config for presets - */ - PresetsConfig presets(); - /** * The config for esbuild loaders https://esbuild.github.io/content-types/ */ @@ -121,58 +108,13 @@ default boolean shouldQuarkusServeBundle() { return !isExternalBundlePath(); } - interface PresetsConfig { - - /** - * Configuration preset to allow defining the web app with scripts and styles to bundle. - * - {web-root}/app/**\/* - *

- * If an index.js/ts is detected, it will be used as entry point for your app. - * If not found the entry point will be auto-generated with all the files in the app directory. - *

- * => processed and added to static/[key].js and static/[key].css (key is "main" by default) - */ - PresetConfig app(); - - /** - * Configuration preset to allow defining web components (js + style + html) as a bundle. - * Convention is to use: - * - /{web-root}/components/[name]/[name].js/ts - * - /{web-root}/components/[name]/[name].scss/css - * - /{web-root}/components/[name]/[name].html (Qute tag) - *

- * => processed and added to static/[key].js and static/[key].css (key is "main" by default) - */ - PresetConfig components(); - - } - - interface PresetConfig { - - /** - * Enable or disable this preset - * - * @return - */ - @WithParentName - @WithDefault("true") - boolean enabled(); - - /** - * The entry point key used for this preset (used in the output) - */ - @WithDefault("main") - Optional entryPointKey(); - } - interface WebDependenciesConfig { /** - * The type used to collect web dependencies: - * web-jar or mvnpm + * Path to the node_modules directory (relative to the project root). */ - @WithDefault("mvnpm") - WebDependencyType type(); + @ConfigDocDefault("node_modules will be in the build/target directory") + Optional nodeModules(); /** * If enabled web dependencies will also be served, this is usually not needed as they are already bundled. @@ -283,14 +225,14 @@ interface EntryPointConfig { /** * The directory for this entry point under the web root. - * By default, it will use the bundle map key. */ + @ConfigDocDefault("the bundle map key") Optional dir(); /** - * The key for this entry point - * By default, it will use the bundle map key. + * The key for this entry point (use the same key as another to bundle them together). */ + @ConfigDocDefault("the bundle map key") Optional key(); default String effectiveDir(String mapKey) { @@ -303,19 +245,4 @@ default String effectiveKey(String mapKey) { } - enum WebDependencyType { - WEBJARS("org.webjars.npm"::equals), - MVNPM(s -> s.startsWith("org.mvnpm")); - - private final Predicate groupMatcher; - - WebDependencyType(Predicate groupMatcher) { - this.groupMatcher = groupMatcher; - } - - public boolean matches(Dependency dep) { - return this.groupMatcher.test(dep.getGroupId()); - } - } - } diff --git a/deployment/src/main/java/io/quarkiverse/web/bundler/deployment/items/BundleConfigAssetsBuildItem.java b/common-deployment/src/main/java/io/quarkiverse/web/bundler/deployment/items/BundleConfigAssetsBuildItem.java similarity index 100% rename from deployment/src/main/java/io/quarkiverse/web/bundler/deployment/items/BundleConfigAssetsBuildItem.java rename to common-deployment/src/main/java/io/quarkiverse/web/bundler/deployment/items/BundleConfigAssetsBuildItem.java diff --git a/deployment/src/main/java/io/quarkiverse/web/bundler/deployment/items/BundleWebAsset.java b/common-deployment/src/main/java/io/quarkiverse/web/bundler/deployment/items/BundleWebAsset.java similarity index 100% rename from deployment/src/main/java/io/quarkiverse/web/bundler/deployment/items/BundleWebAsset.java rename to common-deployment/src/main/java/io/quarkiverse/web/bundler/deployment/items/BundleWebAsset.java diff --git a/deployment/src/main/java/io/quarkiverse/web/bundler/deployment/items/DefaultWebAsset.java b/common-deployment/src/main/java/io/quarkiverse/web/bundler/deployment/items/DefaultWebAsset.java similarity index 100% rename from deployment/src/main/java/io/quarkiverse/web/bundler/deployment/items/DefaultWebAsset.java rename to common-deployment/src/main/java/io/quarkiverse/web/bundler/deployment/items/DefaultWebAsset.java diff --git a/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 similarity index 100% rename from deployment/src/main/java/io/quarkiverse/web/bundler/deployment/items/EntryPointBuildItem.java rename to common-deployment/src/main/java/io/quarkiverse/web/bundler/deployment/items/EntryPointBuildItem.java 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 new file mode 100644 index 0000000..a798577 --- /dev/null +++ b/common-deployment/src/main/java/io/quarkiverse/web/bundler/deployment/items/HtmlTemplatesBuildItem.java @@ -0,0 +1,10 @@ +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/deployment/src/main/java/io/quarkiverse/web/bundler/deployment/items/QuteTagsBuildItem.java b/common-deployment/src/main/java/io/quarkiverse/web/bundler/deployment/items/QuteTagsBuildItem.java similarity index 100% rename from deployment/src/main/java/io/quarkiverse/web/bundler/deployment/items/QuteTagsBuildItem.java rename to common-deployment/src/main/java/io/quarkiverse/web/bundler/deployment/items/QuteTagsBuildItem.java diff --git a/deployment/src/main/java/io/quarkiverse/web/bundler/deployment/items/StaticAssetsBuildItem.java b/common-deployment/src/main/java/io/quarkiverse/web/bundler/deployment/items/StaticAssetsBuildItem.java similarity index 100% rename from deployment/src/main/java/io/quarkiverse/web/bundler/deployment/items/StaticAssetsBuildItem.java rename to common-deployment/src/main/java/io/quarkiverse/web/bundler/deployment/items/StaticAssetsBuildItem.java diff --git a/deployment/src/main/java/io/quarkiverse/web/bundler/deployment/items/StylesAssetsBuildItem.java b/common-deployment/src/main/java/io/quarkiverse/web/bundler/deployment/items/StylesAssetsBuildItem.java similarity index 100% rename from deployment/src/main/java/io/quarkiverse/web/bundler/deployment/items/StylesAssetsBuildItem.java rename to common-deployment/src/main/java/io/quarkiverse/web/bundler/deployment/items/StylesAssetsBuildItem.java diff --git a/deployment/src/main/java/io/quarkiverse/web/bundler/deployment/items/WebAsset.java b/common-deployment/src/main/java/io/quarkiverse/web/bundler/deployment/items/WebAsset.java similarity index 90% rename from deployment/src/main/java/io/quarkiverse/web/bundler/deployment/items/WebAsset.java rename to common-deployment/src/main/java/io/quarkiverse/web/bundler/deployment/items/WebAsset.java index 7f7899c..59bbde0 100644 --- a/deployment/src/main/java/io/quarkiverse/web/bundler/deployment/items/WebAsset.java +++ b/common-deployment/src/main/java/io/quarkiverse/web/bundler/deployment/items/WebAsset.java @@ -18,6 +18,10 @@ default byte[] readContentFromFile() { return readTemplateContent(filePath().orElseThrow()); } + default byte[] contentOrReadFromFile() { + return hasContent() ? content() : readContentFromFile(); + } + default boolean hasContent() { return this.content() != null; } diff --git a/deployment/src/main/java/io/quarkiverse/web/bundler/deployment/items/WebAssetsBuildItem.java b/common-deployment/src/main/java/io/quarkiverse/web/bundler/deployment/items/WebAssetsBuildItem.java similarity index 100% rename from deployment/src/main/java/io/quarkiverse/web/bundler/deployment/items/WebAssetsBuildItem.java rename to common-deployment/src/main/java/io/quarkiverse/web/bundler/deployment/items/WebAssetsBuildItem.java diff --git a/deployment/src/main/java/io/quarkiverse/web/bundler/deployment/staticresources/GeneratedStaticResourceBuildItem.java b/common-deployment/src/main/java/io/quarkiverse/web/bundler/deployment/staticresources/GeneratedStaticResourceBuildItem.java similarity index 100% rename from deployment/src/main/java/io/quarkiverse/web/bundler/deployment/staticresources/GeneratedStaticResourceBuildItem.java rename to common-deployment/src/main/java/io/quarkiverse/web/bundler/deployment/staticresources/GeneratedStaticResourceBuildItem.java diff --git a/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 similarity index 100% rename from deployment/src/main/java/io/quarkiverse/web/bundler/deployment/staticresources/GeneratedStaticResourcesProcessor.java rename to common-deployment/src/main/java/io/quarkiverse/web/bundler/deployment/staticresources/GeneratedStaticResourcesProcessor.java diff --git a/deployment/src/main/java/io/quarkiverse/web/bundler/deployment/staticresources/StaticResourcesDevContext.java b/common-deployment/src/main/java/io/quarkiverse/web/bundler/deployment/staticresources/StaticResourcesDevContext.java similarity index 100% rename from deployment/src/main/java/io/quarkiverse/web/bundler/deployment/staticresources/StaticResourcesDevContext.java rename to common-deployment/src/main/java/io/quarkiverse/web/bundler/deployment/staticresources/StaticResourcesDevContext.java diff --git a/deployment/src/main/java/io/quarkiverse/web/bundler/deployment/util/PathUtils.java b/common-deployment/src/main/java/io/quarkiverse/web/bundler/deployment/util/PathUtils.java similarity index 100% rename from deployment/src/main/java/io/quarkiverse/web/bundler/deployment/util/PathUtils.java rename to common-deployment/src/main/java/io/quarkiverse/web/bundler/deployment/util/PathUtils.java diff --git a/deployment/pom.xml b/deployment/pom.xml index 78a54e2..37228d9 100644 --- a/deployment/pom.xml +++ b/deployment/pom.xml @@ -18,14 +18,6 @@ quarkus-web-bundler ${project.version} - - io.quarkus - quarkus-vertx-http-deployment - - - io.quarkus - quarkus-qute-deployment - io.quarkiverse.web-bundler quarkus-web-bundler-sass-compiler @@ -36,16 +28,26 @@ yuicompressor ${yuicompressor.version} + + + io.quarkus.qute + qute-core io.mvnpm esbuild-java ${esbuild-java.version} + + io.quarkiverse.web-bundler + quarkus-web-bundler-common-deployment + ${project.version} + io.quarkus quarkus-junit5-internal diff --git a/deployment/src/main/java/io/quarkiverse/web/bundler/deployment/ScriptDependenciesScannerProcessor.java b/deployment/src/main/java/io/quarkiverse/web/bundler/deployment/ScriptDependenciesScannerProcessor.java deleted file mode 100644 index a94010f..0000000 --- a/deployment/src/main/java/io/quarkiverse/web/bundler/deployment/ScriptDependenciesScannerProcessor.java +++ /dev/null @@ -1,36 +0,0 @@ -package io.quarkiverse.web.bundler.deployment; - -import java.io.IOException; -import java.nio.file.Path; -import java.util.List; -import java.util.stream.Collectors; - -import org.jboss.logging.Logger; - -import io.quarkiverse.web.bundler.deployment.items.WebDependenciesBuildItem; -import io.quarkus.deployment.annotations.BuildStep; -import io.quarkus.deployment.builditem.ApplicationArchivesBuildItem; -import io.quarkus.deployment.pkg.builditem.CurateOutcomeBuildItem; -import io.quarkus.maven.dependency.Dependency; -import io.quarkus.maven.dependency.ResolvedDependency; -import io.quarkus.paths.PathCollection; - -class ScriptDependenciesScannerProcessor { - - private static final Logger LOGGER = Logger.getLogger(ScriptDependenciesScannerProcessor.class); - - @BuildStep - WebDependenciesBuildItem collectDependencies(ApplicationArchivesBuildItem applicationArchives, - CurateOutcomeBuildItem curateOutcome, - WebBundlerConfig config) - throws IOException { - List webDeps = curateOutcome.getApplicationModel().getRuntimeDependencies().stream() - .filter(Dependency::isJar) - .filter(config.dependencies().type()::matches) - .map(ResolvedDependency::getResolvedPaths) - .flatMap(PathCollection::stream) - .filter(p -> p.getFileName().toString().endsWith(".jar")) - .collect(Collectors.toList()); - return new WebDependenciesBuildItem(config.dependencies().type(), webDeps); - } -} 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 4b23809..28b5b9c 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 @@ -5,15 +5,20 @@ 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.surroundWithSlashes; -import static io.quarkiverse.web.bundler.runtime.qute.WebBundlerQuteContextRecorder.WEB_BUNDLER_ID_PREFIX; import static io.quarkus.deployment.annotations.ExecutionTime.STATIC_INIT; import static java.util.Map.entry; import static java.util.Objects.requireNonNull; import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; import java.io.UncheckedIOException; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.Paths; import java.nio.file.StandardCopyOption; import java.nio.file.StandardOpenOption; import java.time.Instant; @@ -33,17 +38,18 @@ import io.mvnpm.esbuild.BundleException; import io.mvnpm.esbuild.Bundler; +import io.mvnpm.esbuild.model.BundleOptions; import io.mvnpm.esbuild.model.BundleOptionsBuilder; import io.mvnpm.esbuild.model.BundleResult; -import io.mvnpm.esbuild.model.BundleType; import io.mvnpm.esbuild.model.EsBuildConfig; import io.mvnpm.esbuild.model.EsBuildConfigBuilder; +import io.mvnpm.esbuild.model.WebDependency; import io.quarkiverse.web.bundler.deployment.WebBundlerConfig.LoadersConfig; import io.quarkiverse.web.bundler.deployment.items.BundleConfigAssetsBuildItem; import io.quarkiverse.web.bundler.deployment.items.BundleWebAsset; import io.quarkiverse.web.bundler.deployment.items.EntryPointBuildItem; import io.quarkiverse.web.bundler.deployment.items.GeneratedBundleBuildItem; -import io.quarkiverse.web.bundler.deployment.items.QuteTagsBuildItem; +import io.quarkiverse.web.bundler.deployment.items.HtmlTemplatesBuildItem; import io.quarkiverse.web.bundler.deployment.items.StaticAssetsBuildItem; import io.quarkiverse.web.bundler.deployment.items.WebAsset; import io.quarkiverse.web.bundler.deployment.items.WebDependenciesBuildItem; @@ -52,9 +58,6 @@ import io.quarkiverse.web.bundler.runtime.Bundle; import io.quarkiverse.web.bundler.runtime.WebBundlerBuildRecorder; import io.quarkiverse.web.bundler.runtime.WebDependenciesBlockerRecorder; -import io.quarkiverse.web.bundler.runtime.qute.WebBundlerQuteContextRecorder; -import io.quarkiverse.web.bundler.runtime.qute.WebBundlerQuteContextRecorder.WebBundlerQuteContext; -import io.quarkiverse.web.bundler.runtime.qute.WebBundlerQuteEngineObserver; import io.quarkiverse.web.bundler.sass.SassBuildTimeCompiler; import io.quarkus.arc.deployment.AdditionalBeanBuildItem; import io.quarkus.arc.deployment.SyntheticBeanBuildItem; @@ -66,6 +69,7 @@ import io.quarkus.deployment.builditem.LiveReloadBuildItem; import io.quarkus.deployment.pkg.builditem.OutputTargetBuildItem; import io.quarkus.deployment.util.FileUtil; +import io.quarkus.qute.*; import io.quarkus.runtime.LaunchMode; import io.quarkus.runtime.configuration.ConfigurationException; import io.quarkus.vertx.http.deployment.RouteBuildItem; @@ -120,8 +124,8 @@ void bundle(WebBundlerConfig config, && bundlesBuildContext != null && bundlesBuildContext.bundleDistDir() != null; if (isLiveReload - && webDependencies.getDependencies().equals(bundlesBuildContext.webDependencies()) - && !liveReload.getChangedResources().contains("web/tsconfig.json") + && Objects.equals(webDependencies.getDependencies(), bundlesBuildContext.webDependencies()) + && !liveReload.getChangedResources().contains(config.fromWebRoot("tsconfig.json")) && entryPoints.equals(bundlesBuildContext.entryPoints()) && entryPoints.stream().map(EntryPointBuildItem::getWebAssets).flatMap(List::stream) .map(WebAsset::resourceName) @@ -134,8 +138,8 @@ void bundle(WebBundlerConfig config, } boolean hasScssChange = isLiveReload && liveReload.getChangedResources().stream().anyMatch(WebBundlerProcessor::isSassFile); - final BundleType type = BundleType.valueOf(webDependencies.getType().toString()); final Path targetDir = outputTarget.getOutputDirectory().resolve(TARGET_DIR_NAME); + final Path nodeModulesDir = resolveNodeModulesDir(config, outputTarget); try { if (!isLiveReload) { FileUtil.deleteDirectory(targetDir); @@ -167,11 +171,11 @@ void bundle(WebBundlerConfig config, } else { esBuildConfigBuilder.addExternal(join(config.httpRootPath(), "static/*")); } - final BundleOptionsBuilder options = new BundleOptionsBuilder() - .setWorkFolder(targetDir) + final BundleOptionsBuilder optionsBuilder = new BundleOptionsBuilder() + .withWorkDir(targetDir) .withDependencies(webDependencies.getDependencies()) .withEsConfig(esBuildConfigBuilder.build()) - .withType(type); + .withNodeModulesDir(nodeModulesDir); int addedEntryPoints = 0; for (EntryPointBuildItem entryPoint : entryPoints) { final List scripts = new ArrayList<>(); @@ -208,27 +212,32 @@ void bundle(WebBundlerConfig config, LOGGER.infof("Bundling '%s' (%d files)", entryPoint.getEntryPointKey(), scripts.size()); } - if (scripts.size() > 0) { - options.addAutoEntryPoint(targetDir, entryPoint.getEntryPointKey(), scripts); + if (!scripts.isEmpty()) { + optionsBuilder.addAutoEntryPoint(targetDir, entryPoint.getEntryPointKey(), scripts); addedEntryPoints++; } } if (addedEntryPoints > 0) { - + final BundleOptions options = optionsBuilder.build(); if (!isLiveReload || !Objects.equals(webDependencies.getDependencies(), bundlesBuildContext.webDependencies())) { long startedInstall = Instant.now().toEpochMilli(); - Bundler.install(targetDir, webDependencies.getDependencies(), type); - final long duration = Instant.now().minusMillis(startedInstall).toEpochMilli(); - if (LOGGER.isDebugEnabled()) { - String deps = webDependencies.getDependencies().stream().map(Path::getFileName).map(Path::toString) - .collect( - Collectors.joining(", ")); - LOGGER.infof("%d %s Web dependencies installed in %sms: %s", webDependencies.getDependencies().size(), - webDependencies.getType(), duration, deps); + if (Bundler.install(targetDir, options)) { + final long duration = Instant.now().minusMillis(startedInstall).toEpochMilli(); + if (LOGGER.isDebugEnabled()) { + String deps = webDependencies.getDependencies().stream().map(WebDependency::id) + .collect( + Collectors.joining(", ")); + LOGGER.infof("%d web dependencies installed in %sms: %s", webDependencies.getDependencies().size(), + duration, deps); + } else { + LOGGER.infof("%d web Dependencies installed in %sms.", webDependencies.getDependencies().size(), + duration); + } + } else if (webDependencies.getDependencies().isEmpty()) { + LOGGER.info("No web dependencies to install."); } else { - LOGGER.infof("%d %s Web Dependencies installed in %sms ", webDependencies.getDependencies().size(), - webDependencies.getType(), duration); + LOGGER.info("All web dependencies are already installed."); } } final long startedBundling = Instant.now().toEpochMilli(); @@ -240,7 +249,7 @@ && isCompiledSassFile(p.getFileName().toString()))) { stream.forEach(p -> convertToScss(p, targetDir)); } } - final BundleResult result = Bundler.bundle(options.build()); + final BundleResult result = Bundler.bundle(options, false); if (!result.result().output().isBlank()) { LOGGER.debugf(result.result().output()); } @@ -251,7 +260,7 @@ && isCompiledSassFile(p.getFileName().toString()))) { new BundlesBuildContext(webDependencies.getDependencies(), entryPoints, result.dist())); } else { liveReload.setContextObject(BundlesBuildContext.class, new BundlesBuildContext()); - LOGGER.debugf("No entrypoint found, no bundle generated"); + LOGGER.debugf("No entrypoint found, no bundle generated."); } } catch (IOException e) { @@ -262,6 +271,22 @@ && isCompiledSassFile(p.getFileName().toString()))) { } } + private static Path resolveNodeModulesDir(WebBundlerConfig config, OutputTargetBuildItem outputTarget) { + if (config.dependencies().nodeModules().isEmpty()) { + return outputTarget.getOutputDirectory().resolve(BundleOptions.NODE_MODULES); + } + final Path projectRoot = findProjectRoot(outputTarget.getOutputDirectory()); + final Path nodeModulesDir = Path.of(config.dependencies().nodeModules().get().trim()); + if (nodeModulesDir.isAbsolute() && Files.isDirectory(nodeModulesDir.getParent())) { + return nodeModulesDir; + } + if (projectRoot == null || !Files.isDirectory(projectRoot)) { + throw new IllegalStateException( + "If not absolute, the node_modules directory is resolved relative to the project root, but Web Bundler was not able to find the project root."); + } + return projectRoot.resolve(nodeModulesDir); + } + private Map computeLoaders(WebBundlerConfig config) { Map loaders = new HashMap<>(); for (EsBuildConfig.Loader loader : EsBuildConfig.Loader.values()) { @@ -350,24 +375,43 @@ void processStaticWebAssets(WebBundlerConfig config, } @BuildStep - @Record(STATIC_INIT) - void initQuteTags( - BuildProducer additionalBeans, - BuildProducer syntheticBeans, - WebBundlerQuteContextRecorder recorder, - QuteTagsBuildItem quteTags) { - final Map templates = new HashMap<>(); - final List tags = new ArrayList<>(); - for (WebAsset webAsset : quteTags.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())); - tags.add(tagName); + void processHtmlTemplateWebAssets(WebBundlerConfig config, + HtmlTemplatesBuildItem htmlTemplates, + GeneratedBundleBuildItem generatedBundle, + BuildProducer staticResourceProducer, + LiveReloadBuildItem liveReload) { + 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(new NamespaceResolver.NamespaceResolverImpl("inject", 0, (c) -> { + if (c.getName().equals("bundle")) { + return CompletedStage.of(new Bundle(mapping)); + } + return null; + })) + .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.contentOrReadFromFile(); + final String content = engine.parse(new String(bytes, webAsset.charset())).render(); + makeWebAssetPublic(staticResourceProducer, webAsset.pathFromWebRoot(config.webRoot()), liveReload, + HtmlPageWebAsset.of(webAsset, content)); } - additionalBeans.produce(new AdditionalBeanBuildItem(WebBundlerQuteEngineObserver.class)); - syntheticBeans.produce(SyntheticBeanBuildItem.configure(WebBundlerQuteContext.class) - .supplier(recorder.createContext(tags, templates)) - .done()); } @BuildStep @@ -406,7 +450,7 @@ private static void makeWebAssetPublic( staticResourceProducer, Set.of(new GeneratedStaticResourceBuildItem.Source(webAsset.resourceName(), webAsset.filePath())), publicPath, - webAsset.readContentFromFile(), + webAsset.contentOrReadFromFile(), liveReload.isLiveReload() && liveReload.getChangedResources().contains(webAsset.resourceName()), WatchMode.RESTART); } @@ -453,11 +497,71 @@ public static boolean isSassFile(String filename) { return lc.endsWith(".scss") || lc.endsWith(".sass"); } - public record BundlesBuildContext(List webDependencies, List entryPoints, + public record BundlesBuildContext(List webDependencies, List entryPoints, Path bundleDistDir) { public BundlesBuildContext() { this(List.of(), List.of(), null); } } + + static Path findProjectRoot(Path outputDirectory) { + Path currentPath = outputDirectory; + do { + if (Files.exists(currentPath.resolve(Paths.get("src", "main"))) + || Files.exists(currentPath.resolve(Paths.get("config", "application.properties"))) + || Files.exists(currentPath.resolve(Paths.get("config", "application.yaml"))) + || Files.exists(currentPath.resolve(Paths.get("config", "application.yml")))) { + return currentPath.normalize(); + } + if (currentPath.getParent() != null && Files.exists(currentPath.getParent())) { + currentPath = currentPath.getParent(); + } else { + return null; + } + } while (true); + } + + 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/", ""); + 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); + } + + @Override + public Optional getVariant() { + return Optional.empty(); + } + }); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + + } + + 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 Optional filePath() { + return Optional.empty(); + } + + } } 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 new file mode 100644 index 0000000..04bba0a --- /dev/null +++ b/deployment/src/main/java/io/quarkiverse/web/bundler/deployment/WebDependenciesScannerProcessor.java @@ -0,0 +1,39 @@ +package io.quarkiverse.web.bundler.deployment; + +import static io.mvnpm.esbuild.model.WebDependency.WebDependencyType.resolveType; + +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +import org.jboss.logging.Logger; + +import io.mvnpm.esbuild.model.WebDependency; +import io.mvnpm.esbuild.model.WebDependency.WebDependencyType; +import io.quarkiverse.web.bundler.deployment.items.WebDependenciesBuildItem; +import io.quarkus.deployment.annotations.BuildStep; +import io.quarkus.deployment.pkg.builditem.CurateOutcomeBuildItem; +import io.quarkus.maven.dependency.Dependency; +import io.quarkus.maven.dependency.ResolvedDependency; + +class WebDependenciesScannerProcessor { + + private static final Logger LOGGER = Logger.getLogger(WebDependenciesScannerProcessor.class); + + @BuildStep + WebDependenciesBuildItem collectDependencies(CurateOutcomeBuildItem curateOutcome, WebBundlerConfig config) { + List webDeps = curateOutcome.getApplicationModel().getRuntimeDependencies().stream() + .filter(Dependency::isJar) + .filter(d -> WebDependencyType.anyMatch(d.toCompactCoords())) + .map(WebDependenciesScannerProcessor::toWebDep) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + return new WebDependenciesBuildItem(webDeps); + } + + 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())) + .orElse(null); + } +} diff --git a/deployment/src/main/java/io/quarkiverse/web/bundler/deployment/items/WebDependenciesBuildItem.java b/deployment/src/main/java/io/quarkiverse/web/bundler/deployment/items/WebDependenciesBuildItem.java index a1144d0..bca7868 100644 --- a/deployment/src/main/java/io/quarkiverse/web/bundler/deployment/items/WebDependenciesBuildItem.java +++ b/deployment/src/main/java/io/quarkiverse/web/bundler/deployment/items/WebDependenciesBuildItem.java @@ -1,27 +1,20 @@ package io.quarkiverse.web.bundler.deployment.items; -import java.nio.file.Path; import java.util.List; -import io.quarkiverse.web.bundler.deployment.WebBundlerConfig.WebDependencyType; +import io.mvnpm.esbuild.model.WebDependency; import io.quarkus.builder.item.SimpleBuildItem; public final class WebDependenciesBuildItem extends SimpleBuildItem { - private final List dependencies; + private final List dependencies; - private final WebDependencyType type; - - public WebDependenciesBuildItem(WebDependencyType type, List dependencies) { + public WebDependenciesBuildItem(List dependencies) { this.dependencies = dependencies; - this.type = type; } - public List getDependencies() { + public List getDependencies() { return dependencies; } - public WebDependencyType getType() { - return type; - } } diff --git a/docs/modules/ROOT/pages/advanced-guides.adoc b/docs/modules/ROOT/pages/advanced-guides.adoc index 0d26f7e..4421a52 100644 --- a/docs/modules/ROOT/pages/advanced-guides.adoc +++ b/docs/modules/ROOT/pages/advanced-guides.adoc @@ -13,30 +13,18 @@ There are 2 ways to add static files (fonts, images, music, video, ...) to your - Files in `src/main/resources/web/static/**` will be served statically under http://localhost:8080/static/ (you can choose another directory name xref:config-reference.adoc#quarkus-web-bundler_quarkus.web-bundler.static[Config Reference]). For convenience, those static files are excluded (marked as external) from the bundling by default. This allows to reference them from scripts or styles without errors (e.g. `import '/static/foo.png';`). - Other files imported from scripts or styles will be bundled and processed by the configured loaders (see <>) allowing different options (like embedding them as data-url). +[#html-templates] +== html templates rendering +The Web-Bundler is natively integrated with Qute to render html templates at build-time (this won't affect runtime). This way you may create SPA out of the box. For this, just provide an `index.html` file (or any other `.html` file) in the `src/main/resources/web` directory. Combined with the <>, this file will be rendered with the scripts and styles tags to include in your html template. This file will be served as the default index file (e.g. http://localhost:8080/). -NOTE: +IMPORTANT: This rendering is happening at build time, so you won't be able to access any Qute runtime data in this file. If you want to data, you should add the Qute Web extension (or https://quarkiverse.github.io/quarkiverse-docs/quarkus-renarde/dev/[Renarde extension] for MVC with Qute) and use `src/main/resrouces/templates/` directory instead. The <> will also work in this case. == Bundling -=== Presets - -The Web Bundler is pre-configured with presets to make it easy to use out of the box. - -[#preset-app] -==== App - -Directory `src/main/resources/web/app` is destined to contain the scripts, styles and possibly assets for your app. It will be bundled and served into `/static/bundle/main-[hash].[ext].` (see <> for more options) - -[#server-side-web-components] -==== Server Side Web Components (Qute) - -This is not always needed but if you need to add specific script and/or style to your {quarkus-guides-url}/qute-reference#user_tags[Qute tags] (Server Side Web Components). This preset will help you do it elegantly. - -By convention, your component will be defined in `src/main/resources/web/components/[name]/[name].*`. The scripts, styles and assets will be bundled, the html template will be usable as a {quarkus-guides-url}/qute-reference#user_tags[Qute tag]. - -This way all your components scripts/styles will be bundled and you tags will be available in your templates. +By default, directory `src/main/resources/web/app` is destined to contain the scripts, styles and possibly assets for your app. It will be bundled and served into `/static/bundle/main-[hash].[ext].` (see <> for more options). +[#importing-web-dependencies] === Importing Web Dependencies Once added in the pom.xml, the web dependencies can be imported and used with the ESM import syntax, they will automatically be bundled. @@ -83,21 +71,7 @@ import './example.png' [#entry-points] ==== Entry-Points -You may also configure other entry-points: - -.src/main/resources/application.properties -[source,properties] ----- -quarkus.web-bundler.bundle.foo=true // <1> -quarkus.web-bundler.bundle.bar=true // <2> -quarkus.web-bundler.bundle.bar.key=my-key -quarkus.web-bundler.bundle.bar.dir=my-dir - ----- -<1> Bundle `src/main/resources/web/foo/...` into `/static/bundle/foo-[hash].[ext]` -<2> Bundle `src/main/resources/web/my-dir/...` into `/static/bundle/my-key-[hash].[ext]` - -or +You may configure different entry-points (to generate different bundles): .src/main/resources/application.properties [source,properties] @@ -105,22 +79,26 @@ or quarkus.web-bundler.bundle.page-1=true // <1> quarkus.web-bundler.bundle.page-2=true // <2> ---- -<1> Bundle `src/main/resources/web/page-1/...` -<2> Bundle `src/main/resources/web/page-2/...` - -NOTE: As soon as more than one entry-point key is configured, shared code and web dependencies are split off into a separate file. That way if the user first browses to one page and then to another page, they don't have to download all the JavaScript for the second page from scratch if the shared part has already been downloaded and cached by their browser. The path of the shared static script is `/static/bundle/chunk-[hash].js`. +<1> Bundle `src/main/resources/web/page-1/...` into `/static/bundle/page-1-[hash].[ext]` +<2> Bundle `src/main/resources/web/page-2/...` into `/static/bundle/page-2-[hash].[ext]` -This is perfect if you create an app with different pages using different scripts, libraries and styles. +or customize the directory name and bundled file name (and possibly merge multiple directories into one bundle): -You may also split the bundle for the `app` and the `component` presets (by default they will be bundled together in `main`): -.application.properties +.src/main/resources/application.properties [source,properties] ---- -quarkus.web-bundler.presets.app.key=app // <1> -quarkus.web-bundler.presets.components.key=components // <2> +quarkus.web-bundler.bundle.foo=true // <1> +quarkus.web-bundler.bundle.bar=true // <2> +quarkus.web-bundler.bundle.bar.key=my-key +quarkus.web-bundler.bundle.bar.dir=my-dir +quarkus.web-bundler.bundle.baz=true // <1> +quarkus.web-bundler.bundle.baz.key=foo + ---- -<1> The app will be bundled in `/static/bundle/app-[hash].js` -<2> The components will be bundled in `/static/bundle/components-[hash].js` +<1> Bundle `src/main/resources/web/foo/...` and `src/main/resources/web/baz/...` together into `/static/bundle/foo-[hash].[ext]` +<2> Bundle `src/main/resources/web/my-dir/...` into `/static/bundle/my-key-[hash].[ext]` + +NOTE: By default, as soon as more than one entry-point key is configured, shared code and web dependencies are split off into a separate file. That way if the user first browses to one page and then to another page, they don't have to download all the JavaScript for the second page from scratch if the shared part has already been downloaded and cached by their browser. The path of the shared static script is `/static/bundle/chunk-[hash].js`. This setup is perfect if you create an app with different pages using different scripts, libraries and styles. [#loaders] @@ -140,6 +118,29 @@ You can use scss or sass files out of the box. Local import are supported. Impor WARNING: It is currently not possible to import `scss` from the dependencies (https://github.com/quarkiverse/quarkus-web-bundler/issues/58[more info]). +[#qute-components] +=== Server-Side Qute Components + +IMPORTANT: This features requires `quarkus-qute` or `quarkus-qute-web` in the project (and this is not made to be used with the build-time template rendering). + +This is not always needed but if you need to add specific script and/or style to your {quarkus-guides-url}/qute-reference#user_tags[Qute tags] (Server Side Qute Components). This will help you do it elegantly. + +To enable server-side components, add this in the `application.properties`: +[source,properties] +---- +quarkus.web-bundler.bundle.qute-components=true +quarkus.web-bundler.bundle.qute-components.key=main // <1> +quarkus.web-bundler.bundle.qute-components.dir=components // <2> +---- +<1> use `main` to have a single merged bundle with the `app` (or remove this line to use `qute-components` as default) +<2> unless you plan to have another components directory, this is nicer :) + +By convention, your component will be defined in `src/main/resources/web/qute-components/[name]/[name].{html,css,scss,js,ts,...};`. The scripts, styles and assets will be bundled, the html template will be usable as a {quarkus-guides-url}/qute-reference#user_tags[Qute tag]. + +This way all your components scripts/styles will be bundled and your tags will be available in your templates. + +NOTE: If you don't want a single main bundled script, you may create a separate entry point for this with `quarkus.web-bundler.bundle.qute-components.key=components` (see <> for more options). + [#web-dependencies] == Web Dependencies diff --git a/docs/modules/ROOT/pages/includes/quarkus-web-bundler.adoc b/docs/modules/ROOT/pages/includes/quarkus-web-bundler.adoc index 2477553..015845e 100644 --- a/docs/modules/ROOT/pages/includes/quarkus-web-bundler.adoc +++ b/docs/modules/ROOT/pages/includes/quarkus-web-bundler.adoc @@ -22,7 +22,7 @@ endif::add-copy-button-to-env-var[] ifndef::add-copy-button-to-env-var[] Environment variable: `+++QUARKUS_WEB_BUNDLER_WEB_ROOT+++` endif::add-copy-button-to-env-var[] ---|String +--|string |`web` @@ -38,7 +38,7 @@ endif::add-copy-button-to-env-var[] ifndef::add-copy-button-to-env-var[] Environment variable: `+++QUARKUS_WEB_BUNDLER_STATIC+++` endif::add-copy-button-to-env-var[] ---|String +--|string |`static` @@ -58,70 +58,6 @@ endif::add-copy-button-to-env-var[] |`static/bundle` -a|icon:lock[title=Fixed at build time] [[quarkus-web-bundler_quarkus.web-bundler.presets.app]]`link:#quarkus-web-bundler_quarkus.web-bundler.presets.app[quarkus.web-bundler.presets.app]` - -[.description] --- -Enable or disable this preset - -ifdef::add-copy-button-to-env-var[] -Environment variable: env_var_with_copy_button:+++QUARKUS_WEB_BUNDLER_PRESETS_APP+++[] -endif::add-copy-button-to-env-var[] -ifndef::add-copy-button-to-env-var[] -Environment variable: `+++QUARKUS_WEB_BUNDLER_PRESETS_APP+++` -endif::add-copy-button-to-env-var[] ---|boolean -|`true` - - -a|icon:lock[title=Fixed at build time] [[quarkus-web-bundler_quarkus.web-bundler.presets.app.entry-point-key]]`link:#quarkus-web-bundler_quarkus.web-bundler.presets.app.entry-point-key[quarkus.web-bundler.presets.app.entry-point-key]` - -[.description] --- -The entry point key used for this preset (used in the output) - -ifdef::add-copy-button-to-env-var[] -Environment variable: env_var_with_copy_button:+++QUARKUS_WEB_BUNDLER_PRESETS_APP_ENTRY_POINT_KEY+++[] -endif::add-copy-button-to-env-var[] -ifndef::add-copy-button-to-env-var[] -Environment variable: `+++QUARKUS_WEB_BUNDLER_PRESETS_APP_ENTRY_POINT_KEY+++` -endif::add-copy-button-to-env-var[] ---|string -|`main` - - -a|icon:lock[title=Fixed at build time] [[quarkus-web-bundler_quarkus.web-bundler.presets.components]]`link:#quarkus-web-bundler_quarkus.web-bundler.presets.components[quarkus.web-bundler.presets.components]` - -[.description] --- -Enable or disable this preset - -ifdef::add-copy-button-to-env-var[] -Environment variable: env_var_with_copy_button:+++QUARKUS_WEB_BUNDLER_PRESETS_COMPONENTS+++[] -endif::add-copy-button-to-env-var[] -ifndef::add-copy-button-to-env-var[] -Environment variable: `+++QUARKUS_WEB_BUNDLER_PRESETS_COMPONENTS+++` -endif::add-copy-button-to-env-var[] ---|boolean -|`true` - - -a|icon:lock[title=Fixed at build time] [[quarkus-web-bundler_quarkus.web-bundler.presets.components.entry-point-key]]`link:#quarkus-web-bundler_quarkus.web-bundler.presets.components.entry-point-key[quarkus.web-bundler.presets.components.entry-point-key]` - -[.description] --- -The entry point key used for this preset (used in the output) - -ifdef::add-copy-button-to-env-var[] -Environment variable: env_var_with_copy_button:+++QUARKUS_WEB_BUNDLER_PRESETS_COMPONENTS_ENTRY_POINT_KEY+++[] -endif::add-copy-button-to-env-var[] -ifndef::add-copy-button-to-env-var[] -Environment variable: `+++QUARKUS_WEB_BUNDLER_PRESETS_COMPONENTS_ENTRY_POINT_KEY+++` -endif::add-copy-button-to-env-var[] ---|string -|`main` - - a|icon:lock[title=Fixed at build time] [[quarkus-web-bundler_quarkus.web-bundler.loaders.js]]`link:#quarkus-web-bundler_quarkus.web-bundler.loaders.js[quarkus.web-bundler.loaders.js]` [.description] @@ -394,21 +330,20 @@ endif::add-copy-button-to-env-var[] |`true` -a|icon:lock[title=Fixed at build time] [[quarkus-web-bundler_quarkus.web-bundler.dependencies.type]]`link:#quarkus-web-bundler_quarkus.web-bundler.dependencies.type[quarkus.web-bundler.dependencies.type]` +a|icon:lock[title=Fixed at build time] [[quarkus-web-bundler_quarkus.web-bundler.dependencies.node-modules]]`link:#quarkus-web-bundler_quarkus.web-bundler.dependencies.node-modules[quarkus.web-bundler.dependencies.node-modules]` [.description] -- -The type used to collect web dependencies: web-jar or mvnpm +Path to the node_modules directory (relative to the project root). ifdef::add-copy-button-to-env-var[] -Environment variable: env_var_with_copy_button:+++QUARKUS_WEB_BUNDLER_DEPENDENCIES_TYPE+++[] +Environment variable: env_var_with_copy_button:+++QUARKUS_WEB_BUNDLER_DEPENDENCIES_NODE_MODULES+++[] endif::add-copy-button-to-env-var[] ifndef::add-copy-button-to-env-var[] -Environment variable: `+++QUARKUS_WEB_BUNDLER_DEPENDENCIES_TYPE+++` +Environment variable: `+++QUARKUS_WEB_BUNDLER_DEPENDENCIES_NODE_MODULES+++` endif::add-copy-button-to-env-var[] --- a| -`webjars`, `mvnpm` -|`mvnpm` +--|string +|`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]` @@ -464,7 +399,7 @@ a|icon:lock[title=Fixed at build time] [[quarkus-web-bundler_quarkus.web-bundler [.description] -- -The directory for this entry point under the web root. By default, it will use the bundle map key. +The directory for this entry point under the web root. ifdef::add-copy-button-to-env-var[] Environment variable: env_var_with_copy_button:+++QUARKUS_WEB_BUNDLER_BUNDLE__BUNDLE__DIR+++[] @@ -473,14 +408,14 @@ ifndef::add-copy-button-to-env-var[] Environment variable: `+++QUARKUS_WEB_BUNDLER_BUNDLE__BUNDLE__DIR+++` endif::add-copy-button-to-env-var[] --|string -| +|`the bundle map key` a|icon:lock[title=Fixed at build time] [[quarkus-web-bundler_quarkus.web-bundler.bundle.-bundle-.key]]`link:#quarkus-web-bundler_quarkus.web-bundler.bundle.-bundle-.key[quarkus.web-bundler.bundle."bundle".key]` [.description] -- -The key for this entry point By default, it will use the bundle map key. +The key for this entry point (use the same key as another to bundle them together). ifdef::add-copy-button-to-env-var[] Environment variable: env_var_with_copy_button:+++QUARKUS_WEB_BUNDLER_BUNDLE__BUNDLE__KEY+++[] @@ -489,6 +424,6 @@ ifndef::add-copy-button-to-env-var[] Environment variable: `+++QUARKUS_WEB_BUNDLER_BUNDLE__BUNDLE__KEY+++` endif::add-copy-button-to-env-var[] --|string -| +|`the bundle map key` |=== \ No newline at end of file diff --git a/docs/modules/ROOT/pages/index.adoc b/docs/modules/ROOT/pages/index.adoc index 96bf12a..8afc359 100644 --- a/docs/modules/ROOT/pages/index.adoc +++ b/docs/modules/ROOT/pages/index.adoc @@ -10,7 +10,8 @@ No need to install NodeJs, it relies on a Java wrapped version of https://esbuil * [*] Production build * [*] Awesome Dev experience * [*] Integrated with NPM dependencies through xref:advanced-guides.adoc#mvnpm[mvnpm] or xref:advanced-guides.adoc#webjars[WebJars]. -* [*] Server Side Web Components (Qute template + Script + Style) +* [*] Build-time index.html rendering with bundled scripts and styles +* [*] Server Side Qute Components (Qute template + Script + Style) NOTE: The Web Bundler has been pre-configured to reduce the complexity of web bundling. You don't need to know all the concepts of web bundling (entry-points, loaders, ...) to use this extension, it has been pre-configured with sensible defaults that you may change if needed. @@ -51,6 +52,7 @@ WARNING: If you don't import a Web Dependency, it won't be bundled (dead code el Install the bundle in your index.html template: +.web/index.html [source,html] ---- diff --git a/docs/modules/ROOT/pages/main-concepts.adoc b/docs/modules/ROOT/pages/main-concepts.adoc index 1dd04b0..b234d04 100644 --- a/docs/modules/ROOT/pages/main-concepts.adoc +++ b/docs/modules/ROOT/pages/main-concepts.adoc @@ -13,9 +13,7 @@ image:web-bundler.png[Web Bundler schema] 2. Bundles it with the supersonic esbuild compiler (scss are also compiled if needed) and serves them. -3. Make it easy to use the bundle scripts and styles xref:advanced-guides.adoc#bundle-paths[from Qute or any other template engine]. - - +3. Make it easy to create pages (.html) with the bundle scripts and styles xref:advanced-guides.adoc#bundle-paths[using Qute or any other template engine]. [#web-dependencies] == Web Dependencies diff --git a/integration-tests/src/main/resources/application.properties b/integration-tests/src/main/resources/application.properties index 15077ef..e4a9219 100644 --- a/integration-tests/src/main/resources/application.properties +++ b/integration-tests/src/main/resources/application.properties @@ -1,4 +1,7 @@ quarkus.log.category."io.quarkiverse.web.bundler".level=DEBUG quarkus.log.category."io.mvnpm.esbuild".level=DEBUG quarkus.web-bundler.bundle.page-1=true +quarkus.web-bundler.bundle.qute-components=true +quarkus.web-bundler.bundle.qute-components.key=main +quarkus.web-bundler.bundle.qute-components.dir=components quarkus.http.root-path=/foo \ No newline at end of file diff --git a/integration-tests/src/main/resources/web/test.html b/integration-tests/src/main/resources/web/test.html new file mode 100644 index 0000000..959aa5c --- /dev/null +++ b/integration-tests/src/main/resources/web/test.html @@ -0,0 +1,26 @@ + + + + QWA + {#bundle key="chunk"/} + {#bundle tag="style" /} + + + + +

+

Hello QWA

+

Wait for it...

+ + + {#bundle tag="script"/} +
+ + \ No newline at end of file diff --git a/pom.xml b/pom.xml index 7dcd579..a7a6e6a 100644 --- a/pom.xml +++ b/pom.xml @@ -13,8 +13,10 @@ Quarkus Web Bundler - Parent sass-compiler + common-deployment runtime deployment + qute-components docs @@ -30,7 +32,7 @@ UTF-8 3.1.3.Final - 1.0.8 + 1.1.1 4.1.1 2.4.8 diff --git a/qute-components/deployment/pom.xml b/qute-components/deployment/pom.xml new file mode 100644 index 0000000..5f4d70e --- /dev/null +++ b/qute-components/deployment/pom.xml @@ -0,0 +1,52 @@ + + + 4.0.0 + + io.quarkiverse.web-bundler + quarkus-web-bundler-qute-components-parent + 999-SNAPSHOT + + quarkus-web-bundler-qute-components-deployment + Quarkus Web Bundler - Qute Components - Deployment + + + io.quarkus + quarkus-arc-deployment + + + io.quarkiverse.web-bundler + quarkus-web-bundler-common-deployment + ${project.version} + + + io.quarkiverse.web-bundler + quarkus-web-bundler-qute-components + ${project.version} + + + io.quarkus + quarkus-qute-deployment + + + io.quarkus + quarkus-junit5-internal + test + + + + + + maven-compiler-plugin + + + + io.quarkus + quarkus-extension-processor + ${quarkus.version} + + + + + + + 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 new file mode 100644 index 0000000..56bebfa --- /dev/null +++ b/qute-components/deployment/src/main/java/io/quarkiverse/web/bundler/qute/components/deployment/WebBundlerQuteComponentsProcessor.java @@ -0,0 +1,48 @@ +package io.quarkiverse.web.bundler.qute.components.deployment; + +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 org.jboss.logging.Logger; + +import io.quarkiverse.web.bundler.deployment.items.QuteTagsBuildItem; +import io.quarkiverse.web.bundler.deployment.items.WebAsset; +import io.quarkiverse.web.bundler.qute.components.runtime.WebBundlerQuteContextRecorder; +import io.quarkiverse.web.bundler.qute.components.runtime.WebBundlerQuteEngineObserver; +import io.quarkus.arc.deployment.AdditionalBeanBuildItem; +import io.quarkus.arc.deployment.SyntheticBeanBuildItem; +import io.quarkus.deployment.annotations.BuildProducer; +import io.quarkus.deployment.annotations.BuildStep; +import io.quarkus.deployment.annotations.Record; + +class WebBundlerQuteComponentsProcessor { + + private static final Logger LOGGER = Logger.getLogger(WebBundlerQuteComponentsProcessor.class); + + @BuildStep + @Record(STATIC_INIT) + void initQuteTags( + BuildProducer additionalBeans, + BuildProducer syntheticBeans, + WebBundlerQuteContextRecorder recorder, + QuteTagsBuildItem quteTags) { + final Map templates = new HashMap<>(); + final List tags = new ArrayList<>(); + for (WebAsset webAsset : quteTags.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())); + tags.add(tagName); + } + additionalBeans.produce(new AdditionalBeanBuildItem(WebBundlerQuteEngineObserver.class)); + syntheticBeans.produce(SyntheticBeanBuildItem.configure(WebBundlerQuteContextRecorder.WebBundlerQuteContext.class) + .supplier(recorder.createContext(tags, templates)) + .done()); + } + +} diff --git a/qute-components/pom.xml b/qute-components/pom.xml new file mode 100644 index 0000000..9c4de9b --- /dev/null +++ b/qute-components/pom.xml @@ -0,0 +1,16 @@ + + + 4.0.0 + + io.quarkiverse.web-bundler + quarkus-web-bundler-parent + 999-SNAPSHOT + + quarkus-web-bundler-qute-components-parent + Quarkus Web Bundler - Qute Components - Parent + pom + + deployment + runtime + + diff --git a/qute-components/runtime/pom.xml b/qute-components/runtime/pom.xml new file mode 100644 index 0000000..4b23e26 --- /dev/null +++ b/qute-components/runtime/pom.xml @@ -0,0 +1,60 @@ + + + 4.0.0 + + io.quarkiverse.web-bundler + quarkus-web-bundler-qute-components-parent + 999-SNAPSHOT + + quarkus-web-bundler-qute-components + Quarkus Web Bundler - Qute Components - Runtime + + + io.quarkus + quarkus-arc + + + io.quarkus + quarkus-qute + + + io.quarkus + quarkus-vertx-http + + + + + + io.quarkus + quarkus-extension-maven-plugin + ${quarkus.version} + + + compile + + extension-descriptor + + + ${project.groupId}:${project.artifactId}-deployment:${project.version} + + io.quarkus:quarkus-qute + + + + + + + maven-compiler-plugin + + + + io.quarkus + quarkus-extension-processor + ${quarkus.version} + + + + + + + diff --git a/runtime/src/main/java/io/quarkiverse/web/bundler/runtime/qute/WebBundlerQuteContextRecorder.java b/qute-components/runtime/src/main/java/io/quarkiverse/web/bundler/qute/components/runtime/WebBundlerQuteContextRecorder.java similarity index 94% rename from runtime/src/main/java/io/quarkiverse/web/bundler/runtime/qute/WebBundlerQuteContextRecorder.java rename to qute-components/runtime/src/main/java/io/quarkiverse/web/bundler/qute/components/runtime/WebBundlerQuteContextRecorder.java index c461612..d5e458c 100644 --- a/runtime/src/main/java/io/quarkiverse/web/bundler/runtime/qute/WebBundlerQuteContextRecorder.java +++ b/qute-components/runtime/src/main/java/io/quarkiverse/web/bundler/qute/components/runtime/WebBundlerQuteContextRecorder.java @@ -1,4 +1,4 @@ -package io.quarkiverse.web.bundler.runtime.qute; +package io.quarkiverse.web.bundler.qute.components.runtime; import java.util.List; import java.util.Map; diff --git a/runtime/src/main/java/io/quarkiverse/web/bundler/runtime/qute/WebBundlerQuteEngineObserver.java b/qute-components/runtime/src/main/java/io/quarkiverse/web/bundler/qute/components/runtime/WebBundlerQuteEngineObserver.java similarity index 86% rename from runtime/src/main/java/io/quarkiverse/web/bundler/runtime/qute/WebBundlerQuteEngineObserver.java rename to qute-components/runtime/src/main/java/io/quarkiverse/web/bundler/qute/components/runtime/WebBundlerQuteEngineObserver.java index 9a03a8b..444ee4d 100644 --- a/runtime/src/main/java/io/quarkiverse/web/bundler/runtime/qute/WebBundlerQuteEngineObserver.java +++ b/qute-components/runtime/src/main/java/io/quarkiverse/web/bundler/qute/components/runtime/WebBundlerQuteEngineObserver.java @@ -1,6 +1,6 @@ -package io.quarkiverse.web.bundler.runtime.qute; +package io.quarkiverse.web.bundler.qute.components.runtime; -import static io.quarkiverse.web.bundler.runtime.qute.WebBundlerQuteContextRecorder.WEB_BUNDLER_ID_PREFIX; +import static io.quarkiverse.web.bundler.qute.components.runtime.WebBundlerQuteContextRecorder.WEB_BUNDLER_ID_PREFIX; import java.io.Reader; import java.io.StringReader; @@ -12,7 +12,7 @@ import org.jboss.logging.Logger; -import io.quarkiverse.web.bundler.runtime.qute.WebBundlerQuteContextRecorder.WebBundlerQuteContext; +import io.quarkiverse.web.bundler.qute.components.runtime.WebBundlerQuteContextRecorder.WebBundlerQuteContext; import io.quarkus.qute.EngineBuilder; import io.quarkus.qute.TemplateLocator; import io.quarkus.qute.UserTagSectionHelper; diff --git a/qute-components/runtime/src/main/resources/META-INF/quarkus-extension.yaml b/qute-components/runtime/src/main/resources/META-INF/quarkus-extension.yaml new file mode 100644 index 0000000..5b2f0a2 --- /dev/null +++ b/qute-components/runtime/src/main/resources/META-INF/quarkus-extension.yaml @@ -0,0 +1,12 @@ +name: Web Bundler - Qute Components +description: Add support for Qute components to the Web Bundler +metadata: + unlisted: true + keywords: + - web-bundler + - qute + - components + guide: https://docs.quarkiverse.io/quarkus-web-bundler/dev/ + categories: + - "web" + status: "experimental" diff --git a/runtime/pom.xml b/runtime/pom.xml index 99888c5..c63383e 100644 --- a/runtime/pom.xml +++ b/runtime/pom.xml @@ -18,8 +18,10 @@ quarkus-vertx-http - io.quarkus - quarkus-qute + io.quarkiverse.web-bundler + quarkus-web-bundler-qute-components + ${project.version} + true @@ -30,7 +32,7 @@ ${quarkus.version} - compile + process-resources extension-descriptor