diff --git a/extensions/webjars-locator/deployment/pom.xml b/extensions/webjars-locator/deployment/pom.xml
index d2e2c75ca3f28..c7e61954ff64b 100644
--- a/extensions/webjars-locator/deployment/pom.xml
+++ b/extensions/webjars-locator/deployment/pom.xml
@@ -16,6 +16,7 @@
2.24.0
1.13.0
+ 4.5.2
@@ -31,6 +32,10 @@
io.quarkus
quarkus-webjars-locator
+
+ io.mvnpm
+ importmap
+
@@ -63,6 +68,13 @@
3.0.6
test
+
+ org.mvnpm
+ bootstrap
+ ${mvnpm.bootstrap.version}
+ test
+
+
io.quarkus
quarkus-resteasy-deployment
@@ -90,6 +102,7 @@
${webjar.jquery-ui.version}
${webjar.momentjs.version}
+ ${mvnpm.bootstrap.version}
diff --git a/extensions/webjars-locator/deployment/src/main/java/io/quarkus/webjar/locator/deployment/ImportMapBuildItem.java b/extensions/webjars-locator/deployment/src/main/java/io/quarkus/webjar/locator/deployment/ImportMapBuildItem.java
new file mode 100644
index 0000000000000..9b951366ab031
--- /dev/null
+++ b/extensions/webjars-locator/deployment/src/main/java/io/quarkus/webjar/locator/deployment/ImportMapBuildItem.java
@@ -0,0 +1,15 @@
+package io.quarkus.webjar.locator.deployment;
+
+import io.quarkus.builder.item.SimpleBuildItem;
+
+public final class ImportMapBuildItem extends SimpleBuildItem {
+ private final String importmap;
+
+ public ImportMapBuildItem(String importmap) {
+ this.importmap = importmap;
+ }
+
+ public String getImportMap() {
+ return this.importmap;
+ }
+}
diff --git a/extensions/webjars-locator/deployment/src/main/java/io/quarkus/webjar/locator/deployment/WebJarLocatorConfig.java b/extensions/webjars-locator/deployment/src/main/java/io/quarkus/webjar/locator/deployment/WebJarLocatorConfig.java
new file mode 100644
index 0000000000000..2676e483b2c79
--- /dev/null
+++ b/extensions/webjars-locator/deployment/src/main/java/io/quarkus/webjar/locator/deployment/WebJarLocatorConfig.java
@@ -0,0 +1,26 @@
+package io.quarkus.webjar.locator.deployment;
+
+import java.util.Map;
+
+import io.quarkus.runtime.annotations.ConfigItem;
+import io.quarkus.runtime.annotations.ConfigRoot;
+
+/**
+ * Build time configuration for WebJar Locator.
+ */
+@ConfigRoot
+public class WebJarLocatorConfig {
+
+ /**
+ * If the version reroute is enabled.
+ */
+ @ConfigItem(defaultValue = "true")
+ public boolean versionReroute;
+
+ /**
+ * User defined import mappings
+ */
+ @ConfigItem
+ public Map importMappings;
+
+}
diff --git a/extensions/webjars-locator/deployment/src/main/java/io/quarkus/webjar/locator/deployment/WebJarLocatorStandaloneBuildStep.java b/extensions/webjars-locator/deployment/src/main/java/io/quarkus/webjar/locator/deployment/WebJarLocatorStandaloneBuildStep.java
index 95dd06ed48fb7..bf3af18e035a1 100644
--- a/extensions/webjars-locator/deployment/src/main/java/io/quarkus/webjar/locator/deployment/WebJarLocatorStandaloneBuildStep.java
+++ b/extensions/webjars-locator/deployment/src/main/java/io/quarkus/webjar/locator/deployment/WebJarLocatorStandaloneBuildStep.java
@@ -2,16 +2,20 @@
import java.io.IOException;
import java.io.UncheckedIOException;
+import java.net.MalformedURLException;
+import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
-import java.util.Collections;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import java.util.Set;
import java.util.stream.Stream;
import org.jboss.logging.Logger;
+import io.mvnpm.importmap.Aggregator;
import io.quarkus.bootstrap.classloading.ClassPathElement;
import io.quarkus.bootstrap.classloading.QuarkusClassLoader;
import io.quarkus.deployment.Feature;
@@ -26,75 +30,142 @@
import io.quarkus.vertx.http.deployment.RouteBuildItem;
import io.quarkus.vertx.http.runtime.HttpBuildTimeConfig;
import io.quarkus.webjar.locator.runtime.WebJarLocatorRecorder;
+import io.vertx.core.Handler;
+import io.vertx.ext.web.RoutingContext;
public class WebJarLocatorStandaloneBuildStep {
private static final String WEBJARS_PREFIX = "META-INF/resources/webjars";
+ private static final String WEBJARS_NAME = "webjars";
+
+ private static final String MVNPM_PREFIX = "META-INF/resources/_static";
+ private static final String MVNPM_NAME = "mvnpm";
+
private static final Logger log = Logger.getLogger(WebJarLocatorStandaloneBuildStep.class.getName());
@BuildStep
@Record(ExecutionTime.RUNTIME_INIT)
public void findWebjarsAndCreateHandler(
+ WebJarLocatorConfig config,
HttpBuildTimeConfig httpConfig,
BuildProducer feature,
BuildProducer routes,
+ BuildProducer im,
CurateOutcomeBuildItem curateOutcome,
WebJarLocatorRecorder recorder) throws Exception {
- final List providers = QuarkusClassLoader.getElements(WEBJARS_PREFIX, false);
- Map webjarNameToVersionMap = Collections.emptyMap();
+ LibInfo webjarsLibInfo = getLibInfo(curateOutcome, WEBJARS_PREFIX, WEBJARS_NAME);
+ LibInfo mvnpmNameLibInfo = getLibInfo(curateOutcome, MVNPM_PREFIX, MVNPM_NAME);
+
+ if (webjarsLibInfo != null || mvnpmNameLibInfo != null) {
+ feature.produce(new FeatureBuildItem(Feature.WEBJARS_LOCATOR));
+
+ if (webjarsLibInfo != null) {
+ if (config.versionReroute) {
+ Handler handler = recorder.getHandler(getRootPath(httpConfig, "webjars"),
+ webjarsLibInfo.nameVersionMap);
+ routes.produce(RouteBuildItem.builder().route("/webjars/*").handler(handler).build());
+ }
+ } else {
+ log.warn(
+ "No WebJars were found in the project. Requests to the /webjars/ path will always return 404 (Not Found)");
+ }
+ if (mvnpmNameLibInfo != null) {
+ if (config.versionReroute) {
+ Handler handler = recorder.getHandler(getRootPath(httpConfig, "_static"),
+ mvnpmNameLibInfo.nameVersionMap);
+ routes.produce(RouteBuildItem.builder().route("/_static/*").handler(handler).build());
+ }
+ // Also create a importmap endpoint
+ Aggregator aggregator = new Aggregator(mvnpmNameLibInfo.jars);
+ if (!config.importMappings.isEmpty()) {
+ aggregator.addMappings(config.importMappings);
+ }
+
+ String importMap = aggregator.aggregateAsJson(false);
+ im.produce(new ImportMapBuildItem(importMap));
+ String path = getRootPath(httpConfig, IMPORTMAP_ROOT) + IMPORTMAP_FILENAME;
+ Handler importMapHandler = recorder.getImportMapHandler(path,
+ importMap);
+ routes.produce(
+ RouteBuildItem.builder().route("/" + IMPORTMAP_ROOT + "/" + IMPORTMAP_FILENAME)
+ .handler(importMapHandler).build());
+ } else {
+ log.warn(
+ "No Mvnpm jars were found in the project. Requests to the /_static/ path will always return 404 (Not Found)");
+ }
+ }
+ }
+
+ private LibInfo getLibInfo(CurateOutcomeBuildItem curateOutcome, String prefix, String name) {
+
+ final List providers = QuarkusClassLoader.getElements(prefix, false);
if (!providers.isEmpty()) {
- final Map webJarKeys = new HashMap<>(providers.size());
+ final Map keys = new HashMap<>(providers.size());
for (ClassPathElement provider : providers) {
- if (provider.getDependencyKey() == null || !provider.isRuntime()) {
- log.warn("webjars content found in " + provider.getRoot()
- + " won't be available. Please, report this issue.");
- } else {
- webJarKeys.put(provider.getDependencyKey(), provider);
+ if (provider.getDependencyKey() != null && provider.isRuntime()) {
+ keys.put(provider.getDependencyKey(), provider);
}
}
- if (!webJarKeys.isEmpty()) {
- final Map webjarMap = new HashMap<>(webJarKeys.size());
+ if (!keys.isEmpty()) {
+ final Map map = new HashMap<>(keys.size());
+ final Set jars = new HashSet<>();
for (ResolvedDependency dep : curateOutcome.getApplicationModel().getDependencies()) {
if (!dep.isRuntimeCp()) {
continue;
}
- final ClassPathElement provider = webJarKeys.get(dep.getKey());
+
+ final ClassPathElement provider = keys.get(dep.getKey());
if (provider == null) {
continue;
}
provider.apply(tree -> {
- final Path webjarsDir = tree.getPath(WEBJARS_PREFIX);
+ final Path dir = tree.getPath(prefix);
final Path nameDir;
- try (Stream webjarsDirPaths = Files.list(webjarsDir)) {
- nameDir = webjarsDirPaths.filter(Files::isDirectory).findFirst().get();
+ try (Stream dirPaths = Files.list(dir)) {
+ nameDir = dirPaths.filter(Files::isDirectory).findFirst().get();
} catch (IOException e) {
throw new UncheckedIOException(e);
}
if (nameDir == null) {
- log.warn("Failed to determine the name for webjars included in "
+ log.warn("Failed to determine the name for " + name + " included in "
+ tree.getOriginalTree().getRoots());
return null;
}
final String version = Files.isDirectory(nameDir.resolve(dep.getVersion())) ? dep.getVersion() : null;
- webjarMap.put(nameDir.getFileName().toString(), version);
+ map.put(nameDir.getFileName().toString(), version);
+ try {
+ jars.add(dep.getResolvedPaths().getSinglePath().toUri().toURL());
+ } catch (MalformedURLException ex) {
+ throw new RuntimeException(ex);
+ }
return null;
});
}
- webjarNameToVersionMap = webjarMap;
+
+ return new LibInfo(map, jars);
}
}
+ return null;
+ }
- if (!webjarNameToVersionMap.isEmpty()) {
- // The context path + the resources path
- String rootPath = httpConfig.rootPath;
- String webjarRootPath = (rootPath.endsWith("/")) ? rootPath + "webjars/" : rootPath + "/webjars/";
- feature.produce(new FeatureBuildItem(Feature.WEBJARS_LOCATOR));
- routes.produce(
- RouteBuildItem.builder().route("/webjars/*")
- .handler(recorder.getHandler(webjarRootPath, webjarNameToVersionMap)).build());
- } else {
- log.warn("No WebJars were found in the project. Requests to the /webjars/ path will always return 404 (Not Found)");
+ private String getRootPath(HttpBuildTimeConfig httpConfig, String path) {
+ // The context path + the resources path
+ String rootPath = httpConfig.rootPath;
+ return (rootPath.endsWith("/")) ? rootPath + path + "/" : rootPath + "/" + path + "/";
+ }
+
+ static class LibInfo {
+ Map nameVersionMap;
+ Set jars;
+
+ LibInfo(Map nameVersionMap, Set jars) {
+ this.nameVersionMap = nameVersionMap;
+ this.jars = jars;
}
+
}
+
+ private static final String IMPORTMAP_ROOT = "_importmap";
+ private static final String IMPORTMAP_FILENAME = "generated_importmap.js";
}
diff --git a/extensions/webjars-locator/deployment/src/main/java/io/quarkus/webjar/locator/deployment/devui/WebJarLibrariesBuildItem.java b/extensions/webjars-locator/deployment/src/main/java/io/quarkus/webjar/locator/deployment/devui/WebJarLibrariesBuildItem.java
index 87003d19c4908..709d0d21f88c0 100644
--- a/extensions/webjars-locator/deployment/src/main/java/io/quarkus/webjar/locator/deployment/devui/WebJarLibrariesBuildItem.java
+++ b/extensions/webjars-locator/deployment/src/main/java/io/quarkus/webjar/locator/deployment/devui/WebJarLibrariesBuildItem.java
@@ -2,17 +2,22 @@
import java.util.List;
-import io.quarkus.builder.item.SimpleBuildItem;
-
-public final class WebJarLibrariesBuildItem extends SimpleBuildItem {
+import io.quarkus.builder.item.MultiBuildItem;
+public final class WebJarLibrariesBuildItem extends MultiBuildItem {
+ private final String provider;
private final List webJarLibraries;
- public WebJarLibrariesBuildItem(List webJarLibraries) {
+ public WebJarLibrariesBuildItem(String provider, List webJarLibraries) {
+ this.provider = provider;
this.webJarLibraries = webJarLibraries;
}
public List getWebJarLibraries() {
- return webJarLibraries;
+ return this.webJarLibraries;
+ }
+
+ public String getProvider() {
+ return this.provider;
}
}
diff --git a/extensions/webjars-locator/deployment/src/main/java/io/quarkus/webjar/locator/deployment/devui/WebJarLocatorDevModeApiProcessor.java b/extensions/webjars-locator/deployment/src/main/java/io/quarkus/webjar/locator/deployment/devui/WebJarLocatorDevModeApiProcessor.java
index ee17abebef60c..cc2c688e56ea3 100644
--- a/extensions/webjars-locator/deployment/src/main/java/io/quarkus/webjar/locator/deployment/devui/WebJarLocatorDevModeApiProcessor.java
+++ b/extensions/webjars-locator/deployment/src/main/java/io/quarkus/webjar/locator/deployment/devui/WebJarLocatorDevModeApiProcessor.java
@@ -30,7 +30,10 @@
public class WebJarLocatorDevModeApiProcessor {
- private static final String WEBJARS_PREFIX = "META-INF/resources/webjars";
+ private static final String PREFIX = "META-INF/resources/";
+ private static final String WEBJARS_PATH = "webjars";
+ private static final String MVNPM_PATH = "_static";
+
private static final Logger log = Logger.getLogger(WebJarLocatorDevModeApiProcessor.class.getName());
@BuildStep(onlyIf = IsDevelopment.class)
@@ -39,8 +42,18 @@ public void findWebjarsAssets(
CurateOutcomeBuildItem curateOutcome,
BuildProducer webJarLibrariesProducer) {
+ final List webJarLibraries = getLibraries(httpConfig, curateOutcome, WEBJARS_PATH);
+ webJarLibrariesProducer.produce(new WebJarLibrariesBuildItem("webjars", webJarLibraries));
+
+ final List mvnpmLibraries = getLibraries(httpConfig, curateOutcome, MVNPM_PATH);
+ webJarLibrariesProducer.produce(new WebJarLibrariesBuildItem("mvnpm", mvnpmLibraries));
+
+ }
+
+ private List getLibraries(HttpBuildTimeConfig httpConfig,
+ CurateOutcomeBuildItem curateOutcome, String path) {
final List webJarLibraries = new ArrayList<>();
- final List providers = QuarkusClassLoader.getElements(WEBJARS_PREFIX, false);
+ final List providers = QuarkusClassLoader.getElements(PREFIX + path, false);
if (!providers.isEmpty()) {
// Map of webjar artifact keys to class path elements
final Map webJarKeys = providers.stream()
@@ -51,19 +64,21 @@ public void findWebjarsAssets(
// The root path of the application
final String rootPath = httpConfig.rootPath;
// The root path of the webjars
- final String webjarRootPath = (rootPath.endsWith("/")) ? rootPath + "webjars/" : rootPath + "/webjars/";
+ final String webjarRootPath = (rootPath.endsWith("/")) ? rootPath + path + "/" : rootPath + "/" + path + "/";
// For each packaged webjar dependency, create a WebJarLibrary object
curateOutcome.getApplicationModel().getDependencies().stream()
- .map(dep -> createWebJarLibrary(dep, webjarRootPath, webJarKeys))
+ .map(dep -> createWebJarLibrary(dep, webjarRootPath, webJarKeys, path))
.filter(Objects::nonNull).forEach(webJarLibraries::add);
}
}
- webJarLibrariesProducer.produce(new WebJarLibrariesBuildItem(webJarLibraries));
+ return webJarLibraries;
}
- private WebJarLibrary createWebJarLibrary(ResolvedDependency dep, String webjarRootPath,
- Map webJarKeys) {
+ private WebJarLibrary createWebJarLibrary(ResolvedDependency dep,
+ String webjarRootPath,
+ Map webJarKeys,
+ String path) {
// If the dependency is not a runtime class path dependency, return null
if (!dep.isRuntimeCp()) {
return null;
@@ -74,7 +89,7 @@ private WebJarLibrary createWebJarLibrary(ResolvedDependency dep, String webjarR
}
final WebJarLibrary webJarLibrary = new WebJarLibrary(provider.getDependencyKey().getArtifactId());
provider.apply(tree -> {
- final Path webjarsDir = tree.getPath(WEBJARS_PREFIX);
+ final Path webjarsDir = tree.getPath(PREFIX + path);
final Path nameDir;
try (Stream webjarsDirPaths = Files.list(webjarsDir)) {
nameDir = webjarsDirPaths.filter(Files::isDirectory).findFirst().orElseThrow(() -> new IOException(
diff --git a/extensions/webjars-locator/deployment/src/main/java/io/quarkus/webjar/locator/deployment/devui/WebJarLocatorDevUIProcessor.java b/extensions/webjars-locator/deployment/src/main/java/io/quarkus/webjar/locator/deployment/devui/WebJarLocatorDevUIProcessor.java
index 9fb2440b5bdaa..c13cf38105351 100644
--- a/extensions/webjars-locator/deployment/src/main/java/io/quarkus/webjar/locator/deployment/devui/WebJarLocatorDevUIProcessor.java
+++ b/extensions/webjars-locator/deployment/src/main/java/io/quarkus/webjar/locator/deployment/devui/WebJarLocatorDevUIProcessor.java
@@ -1,22 +1,29 @@
package io.quarkus.webjar.locator.deployment.devui;
+import java.util.ArrayList;
import java.util.List;
+import java.util.Optional;
import io.quarkus.deployment.IsDevelopment;
import io.quarkus.deployment.annotations.BuildProducer;
import io.quarkus.deployment.annotations.BuildStep;
import io.quarkus.devui.spi.page.CardPageBuildItem;
import io.quarkus.devui.spi.page.Page;
+import io.quarkus.webjar.locator.deployment.ImportMapBuildItem;
public class WebJarLocatorDevUIProcessor {
@BuildStep(onlyIf = IsDevelopment.class)
public void createPages(BuildProducer cardPageProducer,
- WebJarLibrariesBuildItem webJarLibrariesBuildItem) {
+ List webJarLibrariesBuildItems,
+ Optional importMapBuildItem) {
- CardPageBuildItem cardPageBuildItem = new CardPageBuildItem();
- List webJarLibraries = webJarLibrariesBuildItem.getWebJarLibraries();
+ List webJarLibraries = new ArrayList<>();
+ for (WebJarLibrariesBuildItem webJarLibrariesBuildItem : webJarLibrariesBuildItems) {
+ webJarLibraries.addAll(webJarLibrariesBuildItem.getWebJarLibraries());
+ }
+ CardPageBuildItem cardPageBuildItem = new CardPageBuildItem();
if (!webJarLibraries.isEmpty()) {
// WebJar Libraries
cardPageBuildItem.addBuildTimeData("webJarLibraries", webJarLibraries);
@@ -24,9 +31,21 @@ public void createPages(BuildProducer cardPageProducer,
// WebJar Asset List
cardPageBuildItem.addPage(Page.webComponentPageBuilder()
.componentLink("qwc-webjar-locator-webjar-libraries.js")
- .title("WebJar Libraries")
+ .title("Web libraries")
.icon("font-awesome-solid:folder-tree")
.staticLabel(String.valueOf(webJarLibraries.size())));
+
+ if (importMapBuildItem.isPresent()) {
+ cardPageBuildItem.addBuildTimeData("importMap", importMapBuildItem.get().getImportMap());
+
+ // ImportMap
+ cardPageBuildItem.addPage(Page.webComponentPageBuilder()
+ .componentLink("qwc-webjar-locator-importmap.js")
+ .title("Import Map")
+ .icon("font-awesome-solid:diagram-project"));
+
+ }
+
}
cardPageProducer.produce(cardPageBuildItem);
diff --git a/extensions/webjars-locator/deployment/src/main/resources/dev-ui/qwc-webjar-locator-importmap.js b/extensions/webjars-locator/deployment/src/main/resources/dev-ui/qwc-webjar-locator-importmap.js
new file mode 100644
index 0000000000000..fa732bb3ff901
--- /dev/null
+++ b/extensions/webjars-locator/deployment/src/main/resources/dev-ui/qwc-webjar-locator-importmap.js
@@ -0,0 +1,48 @@
+import {LitElement, html, css} from 'lit';
+import {importMap} from 'build-time-data';
+
+import '@quarkus-webcomponents/codeblock';
+
+export class QwcWebjarLocatorImportmap extends LitElement {
+
+ static styles = css`
+ :host{
+ display: flex;
+ flex-direction: column;
+ gap: 15px;
+ padding: 10px;
+ height: 100%;
+ }
+ `;
+
+ static properties = {
+ _importMap: {type: String}
+ };
+
+ constructor() {
+ super();
+ this._importMap = importMap;
+ }
+
+ render() {
+ return html`
+ To use this in your app, add this to the head of your main html:
+
+
+
+
+
+ Here is the generated import map:
+
+
+
+
+ `;
+ }
+}
+
+customElements.define('qwc-webjar-locator-importmap', QwcWebjarLocatorImportmap)
\ No newline at end of file
diff --git a/extensions/webjars-locator/deployment/src/test/java/io/quarkus/webjar/locator/test/ImportMapTest.java b/extensions/webjars-locator/deployment/src/test/java/io/quarkus/webjar/locator/test/ImportMapTest.java
new file mode 100644
index 0000000000000..02f01cb64b614
--- /dev/null
+++ b/extensions/webjars-locator/deployment/src/test/java/io/quarkus/webjar/locator/test/ImportMapTest.java
@@ -0,0 +1,34 @@
+package io.quarkus.webjar.locator.test;
+
+import static org.hamcrest.Matchers.containsString;
+
+import java.util.List;
+
+import org.jboss.shrinkwrap.api.asset.StringAsset;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
+
+import io.quarkus.maven.dependency.Dependency;
+import io.quarkus.test.QuarkusUnitTest;
+import io.restassured.RestAssured;
+
+public class ImportMapTest extends WebJarLocatorTestSupport {
+ private static final String META_INF_RESOURCES = "META-INF/resources/";
+
+ @RegisterExtension
+ static QuarkusUnitTest runner = new QuarkusUnitTest()
+ .withApplicationRoot((jar) -> jar
+ .addAsResource(new StringAsset("Hello!"), META_INF_RESOURCES + "/index.html")
+ .addAsResource(new StringAsset("Test"), META_INF_RESOURCES + "/some/path/test.txt"))
+ .setForcedDependencies(List.of(
+ Dependency.of("org.mvnpm", "bootstrap", BOOTSTRAP_VERSION)));
+
+ @Test
+ public void test() {
+ // Test normal files
+ RestAssured.get("/_importmap/generated_importmap.js").then()
+ .statusCode(200)
+ .body(containsString("\"bootstrap/\" : \"/_static/bootstrap/" + BOOTSTRAP_VERSION + "/dist/\""));
+
+ }
+}
diff --git a/extensions/webjars-locator/deployment/src/test/java/io/quarkus/webjar/locator/test/WebJarLocatorDevModeTest.java b/extensions/webjars-locator/deployment/src/test/java/io/quarkus/webjar/locator/test/WebJarLocatorDevModeTest.java
index 7949be6326818..fad3ee7262948 100644
--- a/extensions/webjars-locator/deployment/src/test/java/io/quarkus/webjar/locator/test/WebJarLocatorDevModeTest.java
+++ b/extensions/webjars-locator/deployment/src/test/java/io/quarkus/webjar/locator/test/WebJarLocatorDevModeTest.java
@@ -46,12 +46,16 @@ public void testDevMode() {
.statusCode(200);
RestAssured.get("/webjars/momentjs/min/moment.min.js").then()
.statusCode(200);
+ RestAssured.get("/_static/bootstrap/dist/js/bootstrap.min.js").then()
+ .statusCode(200);
// Test using version in url of existing Web Jar
RestAssured.get("/webjars/jquery-ui/" + JQUERY_UI_VERSION + "/jquery-ui.min.js").then()
.statusCode(200);
RestAssured.get("/webjars/momentjs/" + MOMENTJS_VERSION + "/min/moment.min.js").then()
.statusCode(200);
+ RestAssured.get("/_static/bootstrap/" + BOOTSTRAP_VERSION + "/dist/js/bootstrap.min.js").then()
+ .statusCode(200);
// Test non-existing Web Jar
RestAssured.get("/webjars/bootstrap/js/bootstrap.min.js").then()
@@ -60,6 +64,8 @@ public void testDevMode() {
.statusCode(404);
RestAssured.get("/webjars/momentjs/2.25.0/min/moment.min.js").then()
.statusCode(404);
+ RestAssured.get("/_static/foundation-sites/6.8.1/dist/js/foundation.esm.js").then()
+ .statusCode(404);
// Test webjar that does not have a version in the jar path
RestAssured.get("/webjars/dcjs/dc.min.js").then()
@@ -93,12 +99,16 @@ public void testDevMode() {
.statusCode(200);
RestAssured.get("/webjars/momentjs/min/moment.min.js").then()
.statusCode(200);
+ RestAssured.get("/_static/bootstrap/dist/js/bootstrap.min.js").then()
+ .statusCode(200);
// Test using version in url of existing Web Jar
RestAssured.get("/webjars/jquery-ui/" + JQUERY_UI_VERSION + "/jquery-ui.min.js").then()
.statusCode(200);
RestAssured.get("/webjars/momentjs/" + MOMENTJS_VERSION + "/min/moment.min.js").then()
.statusCode(200);
+ RestAssured.get("/_static/bootstrap/" + BOOTSTRAP_VERSION + "/dist/js/bootstrap.min.js").then()
+ .statusCode(200);
// Test non-existing Web Jar
RestAssured.get("/webjars/bootstrap/js/bootstrap.min.js").then()
@@ -107,5 +117,7 @@ public void testDevMode() {
.statusCode(404);
RestAssured.get("/webjars/momentjs/2.25.0/min/moment.min.js").then()
.statusCode(404);
+ RestAssured.get("/_static/foundation-sites/6.8.1/dist/js/foundation.esm.js").then()
+ .statusCode(404);
}
}
diff --git a/extensions/webjars-locator/deployment/src/test/java/io/quarkus/webjar/locator/test/WebJarLocatorRootPathTest.java b/extensions/webjars-locator/deployment/src/test/java/io/quarkus/webjar/locator/test/WebJarLocatorRootPathTest.java
index 26db27c282959..6efa49606eb43 100644
--- a/extensions/webjars-locator/deployment/src/test/java/io/quarkus/webjar/locator/test/WebJarLocatorRootPathTest.java
+++ b/extensions/webjars-locator/deployment/src/test/java/io/quarkus/webjar/locator/test/WebJarLocatorRootPathTest.java
@@ -21,8 +21,10 @@ public class WebJarLocatorRootPathTest extends WebJarLocatorTestSupport {
.addAsResource(new StringAsset("Hello!"), META_INF_RESOURCES + "index.html")
.addAsResource(new StringAsset("Test"), META_INF_RESOURCES + "some/path/test.txt"))
.overrideConfigKey("quarkus.http.root-path", "/app")
- .setForcedDependencies(List.of(Dependency.of("org.webjars", "jquery-ui", JQUERY_UI_VERSION),
- Dependency.of("org.webjars", "momentjs", MOMENTJS_VERSION)));
+ .setForcedDependencies(List.of(
+ Dependency.of("org.webjars", "jquery-ui", JQUERY_UI_VERSION),
+ Dependency.of("org.webjars", "momentjs", MOMENTJS_VERSION),
+ Dependency.of("org.mvnpm", "bootstrap", BOOTSTRAP_VERSION)));
@Test
public void test() {
@@ -43,12 +45,16 @@ public void test() {
.statusCode(200);
RestAssured.get("/webjars/momentjs/min/moment.min.js").then()
.statusCode(200);
+ RestAssured.get("/_static/bootstrap/dist/js/bootstrap.min.js").then()
+ .statusCode(200);
// Test using version in url of existing Web Jar
RestAssured.get("/webjars/jquery-ui/" + JQUERY_UI_VERSION + "/jquery-ui.min.js").then()
.statusCode(200);
RestAssured.get("/webjars/momentjs/" + MOMENTJS_VERSION + "/min/moment.min.js").then()
.statusCode(200);
+ RestAssured.get("/_static/bootstrap/" + BOOTSTRAP_VERSION + "/dist/js/bootstrap.min.js").then()
+ .statusCode(200);
// Test non-existing Web Jar
RestAssured.get("/webjars/bootstrap/js/bootstrap.min.js").then()
@@ -57,6 +63,8 @@ public void test() {
.statusCode(404);
RestAssured.get("/webjars/momentjs/2.25.0/min/moment.min.js").then()
.statusCode(404);
+ RestAssured.get("/_static/foundation-sites/6.8.1/dist/js/foundation.esm.js").then()
+ .statusCode(404);
// Test webjar that does not have a version in the jar path
RestAssured.get("/webjars/dcjs/dc.min.js").then()
diff --git a/extensions/webjars-locator/deployment/src/test/java/io/quarkus/webjar/locator/test/WebJarLocatorTest.java b/extensions/webjars-locator/deployment/src/test/java/io/quarkus/webjar/locator/test/WebJarLocatorTest.java
index 7453c71b3b306..715a2d042f187 100644
--- a/extensions/webjars-locator/deployment/src/test/java/io/quarkus/webjar/locator/test/WebJarLocatorTest.java
+++ b/extensions/webjars-locator/deployment/src/test/java/io/quarkus/webjar/locator/test/WebJarLocatorTest.java
@@ -22,7 +22,8 @@ public class WebJarLocatorTest extends WebJarLocatorTestSupport {
.addAsResource(new StringAsset("Test"), META_INF_RESOURCES + "/some/path/test.txt"))
.setForcedDependencies(List.of(
Dependency.of("org.webjars", "jquery-ui", JQUERY_UI_VERSION),
- Dependency.of("org.webjars", "momentjs", MOMENTJS_VERSION)));
+ Dependency.of("org.webjars", "momentjs", MOMENTJS_VERSION),
+ Dependency.of("org.mvnpm", "bootstrap", BOOTSTRAP_VERSION)));
@Test
public void test() {
@@ -44,12 +45,16 @@ public void test() {
.statusCode(200);
RestAssured.get("/webjars/momentjs/min/moment.min.js").then()
.statusCode(200);
+ RestAssured.get("/_static/bootstrap/dist/js/bootstrap.min.js").then()
+ .statusCode(200);
// Test using version in url of existing Web Jar
RestAssured.get("/webjars/jquery-ui/" + JQUERY_UI_VERSION + "/jquery-ui.min.js").then()
.statusCode(200);
RestAssured.get("/webjars/momentjs/" + MOMENTJS_VERSION + "/min/moment.min.js").then()
.statusCode(200);
+ RestAssured.get("/_static/bootstrap/" + BOOTSTRAP_VERSION + "/dist/js/bootstrap.min.js").then()
+ .statusCode(200);
// Test non-existing Web Jar
RestAssured.get("/webjars/bootstrap/js/bootstrap.min.js").then()
@@ -58,6 +63,8 @@ public void test() {
.statusCode(404);
RestAssured.get("/webjars/momentjs/2.25.0/min/moment.min.js").then()
.statusCode(404);
+ RestAssured.get("/_static/foundation-sites/6.8.1/dist/js/foundation.esm.js").then()
+ .statusCode(404);
// Test webjar that does not have a version in the jar path
RestAssured.get("/webjars/dcjs/dc.min.js").then()
diff --git a/extensions/webjars-locator/deployment/src/test/java/io/quarkus/webjar/locator/test/WebJarLocatorTestSupport.java b/extensions/webjars-locator/deployment/src/test/java/io/quarkus/webjar/locator/test/WebJarLocatorTestSupport.java
index 83e84572f0b8e..0afe0cfae437c 100644
--- a/extensions/webjars-locator/deployment/src/test/java/io/quarkus/webjar/locator/test/WebJarLocatorTestSupport.java
+++ b/extensions/webjars-locator/deployment/src/test/java/io/quarkus/webjar/locator/test/WebJarLocatorTestSupport.java
@@ -4,4 +4,5 @@ class WebJarLocatorTestSupport {
static final String JQUERY_UI_VERSION = System.getProperty("webjar.jquery-ui.version");
static final String MOMENTJS_VERSION = System.getProperty("webjar.momentjs.version");
+ static final String BOOTSTRAP_VERSION = System.getProperty("mvnpm.bootstrap.version");
}
diff --git a/extensions/webjars-locator/runtime/src/main/java/io/quarkus/webjar/locator/runtime/WebJarLocatorRecorder.java b/extensions/webjars-locator/runtime/src/main/java/io/quarkus/webjar/locator/runtime/WebJarLocatorRecorder.java
index 813f4e920f830..55f22be5cf44a 100644
--- a/extensions/webjars-locator/runtime/src/main/java/io/quarkus/webjar/locator/runtime/WebJarLocatorRecorder.java
+++ b/extensions/webjars-locator/runtime/src/main/java/io/quarkus/webjar/locator/runtime/WebJarLocatorRecorder.java
@@ -4,6 +4,8 @@
import io.quarkus.runtime.annotations.Recorder;
import io.vertx.core.Handler;
+import io.vertx.core.http.HttpHeaders;
+import io.vertx.core.http.HttpServerResponse;
import io.vertx.ext.web.RoutingContext;
@Recorder
@@ -42,4 +44,27 @@ public Handler getHandler(String webjarsRootUrl, Map getImportMapHandler(String expectedPath, String importmap) {
+ return new Handler() {
+ @Override
+ public void handle(RoutingContext event) {
+ String path = event.normalizedPath();
+ if (path.equals(expectedPath)) {
+ HttpServerResponse response = event.response();
+ response.headers().set(HttpHeaders.CONTENT_TYPE, "text/javascript");
+ response.end(JAVASCRIPT_CODE.formatted(importmap));
+ } else {
+ // should not happen if route is set up correctly
+ event.next();
+ }
+ }
+ };
+ }
+
+ private static final String JAVASCRIPT_CODE = """
+ const im = document.createElement('script');
+ im.type = 'importmap';
+ im.textContent = JSON.stringify(%s);
+ document.currentScript.after(im);
+ """;
}