Skip to content

Commit

Permalink
Various improvements in user xp
Browse files Browse the repository at this point in the history
  • Loading branch information
ia3andy committed Dec 7, 2023
1 parent 878dcff commit de9b52e
Show file tree
Hide file tree
Showing 40 changed files with 593 additions and 336 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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/).

Expand Down
42 changes: 42 additions & 0 deletions common-deployment/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>io.quarkiverse.web-bundler</groupId>
<artifactId>quarkus-web-bundler-parent</artifactId>
<version>999-SNAPSHOT</version>
</parent>
<artifactId>quarkus-web-bundler-common-deployment</artifactId>
<name>Quarkus Web Bundler - Common - Deployment</name>
<dependencies>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-arc-deployment</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-vertx-http-deployment</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-junit5-internal</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<annotationProcessorPaths>
<path>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-extension-processor</artifactId>
<version>${quarkus.version}</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
</plugins>
</build>
</project>
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -53,6 +54,7 @@ void collect(ApplicationArchivesBuildItem applicationArchives,
BuildProducer<StaticAssetsBuildItem> staticAssets,
BuildProducer<QuteTagsBuildItem> quteTagsAssets,
BuildProducer<BundleConfigAssetsBuildItem> bundleConfigAssets,
BuildProducer<HtmlTemplatesBuildItem> htmlTemplatesAssets,
BuildProducer<HotDeploymentWatchedFileBuildItem> watchedFiles,
WebBundlerConfig config,
LiveReloadBuildItem liveReload)
Expand All @@ -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");
Expand All @@ -76,23 +78,25 @@ void collect(ApplicationArchivesBuildItem applicationArchives,
.filter(Dependency::isRuntimeExtensionArtifact).collect(Collectors.toList());
Map<String, EntryPointConfig> entryPointsConfig = new HashMap<>(config.bundle());
final List<Scanner> staticAssetsScanners = new ArrayList<>();
final List<Scanner> htmlTemplateAssetsScanner = new ArrayList<>();
final List<Scanner> quteTagsAssetsScanners = new ArrayList<>();
final List<Scanner> 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()));
Expand Down Expand Up @@ -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);
}

Expand All @@ -139,9 +145,13 @@ private static boolean hasNewWebResources(WebBundlerConfig config, LiveReloadBui
.allMatch(webAssets::contains);
}

void produceWebAssets(BuildProducer<EntryPointBuildItem> bundles, BuildProducer<StaticAssetsBuildItem> staticAssets,
BuildProducer<QuteTagsBuildItem> quteTagsAssets, BuildProducer<BundleConfigAssetsBuildItem> bundleConfigAssets,
WebAssetsLookupDevContext context, boolean checkIfExists) {
void produceWebAssets(BuildProducer<EntryPointBuildItem> bundles,
BuildProducer<StaticAssetsBuildItem> staticAssets,
BuildProducer<QuteTagsBuildItem> quteTagsAssets,
BuildProducer<BundleConfigAssetsBuildItem> bundleConfigAssets,
BuildProducer<HtmlTemplatesBuildItem> htmlTemplatesAssets,
WebAssetsLookupDevContext context,
boolean checkIfExists) {
for (Map.Entry<String, List<BundleWebAsset>> e : context.bundleAssets().entrySet()) {
bundles.produce(new EntryPointBuildItem(e.getKey(), checkIfExists ? checkWebAssets(e.getValue()) : e.getValue()));
}
Expand All @@ -154,6 +164,9 @@ void produceWebAssets(BuildProducer<EntryPointBuildItem> bundles, BuildProducer<
quteTagsAssets.produce(new QuteTagsBuildItem(
checkIfExists ? checkWebAssets(context.quteWebAssets()) : context.quteWebAssets()));

htmlTemplatesAssets.produce(new HtmlTemplatesBuildItem(
checkIfExists ? checkWebAssets(context.htmlTemplateWebAssets()) : context.htmlTemplateWebAssets()));

}

private static <T extends WebAsset> List<T> checkWebAssets(List<T> webAssets) {
Expand All @@ -163,13 +176,14 @@ private static <T extends WebAsset> List<T> checkWebAssets(List<T> webAssets) {
}

record WebAssetsLookupDevContext(Map<String, List<BundleWebAsset>> bundleAssets, List<WebAsset> staticWebAssets,
List<WebAsset> quteWebAssets, List<WebAsset> bundleConfigWebAssets) {
List<WebAsset> quteWebAssets, List<WebAsset> bundleConfigWebAssets, List<WebAsset> htmlTemplateWebAssets) {

public List<WebAsset> allWebAssets() {
final ArrayList<WebAsset> all = new ArrayList<>();
all.addAll(staticWebAssets);
all.addAll(quteWebAssets);
all.addAll(bundleConfigWebAssets);
all.addAll(htmlTemplateWebAssets);
bundleAssets.values().forEach(all::addAll);
return all;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,25 +1,18 @@
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;
import java.util.List;
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;
Expand All @@ -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<String, EntryPointConfig> bundle();

/**
Expand All @@ -55,7 +48,6 @@ default String fromWebRoot(String dir) {
*/
@WithName("static")
@WithDefault("static")
@Pattern(regexp = "")
String staticDir();

/**
Expand All @@ -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/
*/
Expand Down Expand Up @@ -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/**\/*
* <p>
* 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.
* <p>
* => 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)
* <p>
* => 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<String> 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<String> nodeModules();

/**
* If enabled web dependencies will also be served, this is usually not needed as they are already bundled.
Expand Down Expand Up @@ -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<String> 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<String> key();

default String effectiveDir(String mapKey) {
Expand All @@ -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<String> groupMatcher;

WebDependencyType(Predicate<String> groupMatcher) {
this.groupMatcher = groupMatcher;
}

public boolean matches(Dependency dep) {
return this.groupMatcher.test(dep.getGroupId());
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package io.quarkiverse.web.bundler.deployment.items;

import java.util.List;

public final class HtmlTemplatesBuildItem extends WebAssetsBuildItem {

public HtmlTemplatesBuildItem(List<WebAsset> webAssets) {
super(webAssets);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
Loading

0 comments on commit de9b52e

Please sign in to comment.