Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[breaking] Various improvements in user xp #132

Merged
merged 1 commit into from
Dec 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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