forked from quarkusio/quarkus
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
WebJars locator extension Dev UI support
- Loading branch information
1 parent
ec4074a
commit 5815042
Showing
6 changed files
with
376 additions
and
0 deletions.
There are no files selected for viewing
43 changes: 43 additions & 0 deletions
43
...ator/deployment/src/main/java/io/quarkus/webjar/locator/deployment/devui/WebJarAsset.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
package io.quarkus.webjar.locator.deployment.devui; | ||
|
||
import java.util.List; | ||
|
||
public class WebJarAsset { | ||
|
||
private String name; | ||
private List<WebJarAsset> children; | ||
private boolean fileAsset; | ||
private String urlPart; | ||
|
||
public String getName() { | ||
return name; | ||
} | ||
|
||
public void setName(String name) { | ||
this.name = name; | ||
} | ||
|
||
public List<WebJarAsset> getChildren() { | ||
return children; | ||
} | ||
|
||
public void setChildren(List<WebJarAsset> children) { | ||
this.children = children; | ||
} | ||
|
||
public boolean isFileAsset() { | ||
return fileAsset; | ||
} | ||
|
||
public void setFileAsset(boolean fileAsset) { | ||
this.fileAsset = fileAsset; | ||
} | ||
|
||
public String getUrlPart() { | ||
return urlPart; | ||
} | ||
|
||
public void setUrlPart(String urlPart) { | ||
this.urlPart = urlPart; | ||
} | ||
} |
18 changes: 18 additions & 0 deletions
18
...nt/src/main/java/io/quarkus/webjar/locator/deployment/devui/WebJarLibrariesBuildItem.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
package io.quarkus.webjar.locator.deployment.devui; | ||
|
||
import java.util.List; | ||
|
||
import io.quarkus.builder.item.SimpleBuildItem; | ||
|
||
public final class WebJarLibrariesBuildItem extends SimpleBuildItem { | ||
|
||
private final List<WebJarLibrary> webJarLibraries; | ||
|
||
public WebJarLibrariesBuildItem(List<WebJarLibrary> webJarLibraries) { | ||
this.webJarLibraries = webJarLibraries; | ||
} | ||
|
||
public List<WebJarLibrary> getWebJarLibraries() { | ||
return webJarLibraries; | ||
} | ||
} |
32 changes: 32 additions & 0 deletions
32
...or/deployment/src/main/java/io/quarkus/webjar/locator/deployment/devui/WebJarLibrary.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
package io.quarkus.webjar.locator.deployment.devui; | ||
|
||
public class WebJarLibrary { | ||
|
||
private final String webJarName; | ||
private String version; | ||
private WebJarAsset rootAsset; // must be a list to work with vaadin-grid | ||
|
||
public WebJarLibrary(String webJarName) { | ||
this.webJarName = webJarName; | ||
} | ||
|
||
public String getWebJarName() { | ||
return webJarName; | ||
} | ||
|
||
public String getVersion() { | ||
return version; | ||
} | ||
|
||
public void setVersion(String version) { | ||
this.version = version; | ||
} | ||
|
||
public WebJarAsset getRootAsset() { | ||
return rootAsset; | ||
} | ||
|
||
public void setRootAsset(WebJarAsset rootAsset) { | ||
this.rootAsset = rootAsset; | ||
} | ||
} |
144 changes: 144 additions & 0 deletions
144
...ain/java/io/quarkus/webjar/locator/deployment/devui/WebJarLocatorDevModeApiProcessor.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,144 @@ | ||
package io.quarkus.webjar.locator.deployment.devui; | ||
|
||
import java.io.IOException; | ||
import java.io.UncheckedIOException; | ||
import java.nio.file.DirectoryStream; | ||
import java.nio.file.Files; | ||
import java.nio.file.InvalidPathException; | ||
import java.nio.file.Path; | ||
import java.util.ArrayList; | ||
import java.util.Comparator; | ||
import java.util.HashMap; | ||
import java.util.LinkedList; | ||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.Objects; | ||
import java.util.stream.Collectors; | ||
import java.util.stream.Stream; | ||
|
||
import org.jboss.logging.Logger; | ||
|
||
import io.quarkus.bootstrap.classloading.ClassPathElement; | ||
import io.quarkus.bootstrap.classloading.QuarkusClassLoader; | ||
import io.quarkus.deployment.IsDevelopment; | ||
import io.quarkus.deployment.annotations.BuildProducer; | ||
import io.quarkus.deployment.annotations.BuildStep; | ||
import io.quarkus.deployment.pkg.builditem.CurateOutcomeBuildItem; | ||
import io.quarkus.maven.dependency.ArtifactKey; | ||
import io.quarkus.maven.dependency.ResolvedDependency; | ||
import io.quarkus.vertx.http.runtime.HttpBuildTimeConfig; | ||
|
||
public class WebJarLocatorDevModeApiProcessor { | ||
|
||
private static final String WEBJARS_PREFIX = "META-INF/resources/webjars"; | ||
private static final Logger log = Logger.getLogger(WebJarLocatorDevModeApiProcessor.class.getName()); | ||
|
||
@BuildStep(onlyIf = IsDevelopment.class) | ||
public void findWebjarsAssets( | ||
HttpBuildTimeConfig httpConfig, | ||
CurateOutcomeBuildItem curateOutcome, | ||
BuildProducer<WebJarLibrariesBuildItem> webJarLibrariesProducer) { | ||
|
||
final List<WebJarLibrary> webJarLibraries = new ArrayList<>(); | ||
final List<ClassPathElement> providers = QuarkusClassLoader.getElements(WEBJARS_PREFIX, false); | ||
if (!providers.isEmpty()) { | ||
// Map of webjar artifact keys to class path elements | ||
final Map<ArtifactKey, ClassPathElement> webJarKeys = providers.stream() | ||
.filter(provider -> provider.getDependencyKey() != null && provider.isRuntime()) | ||
.collect(Collectors.toMap(ClassPathElement::getDependencyKey, provider -> provider, (a, b) -> b, | ||
() -> new HashMap<>(providers.size()))); | ||
if (!webJarKeys.isEmpty()) { | ||
// 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/"; | ||
|
||
// For each packaged webjar dependency, create a WebJarLibrary object | ||
curateOutcome.getApplicationModel().getDependencies().stream() | ||
.map(dep -> createWebJarLibrary(dep, webjarRootPath, webJarKeys)) | ||
.filter(Objects::nonNull).forEach(webJarLibraries::add); | ||
} | ||
} | ||
webJarLibrariesProducer.produce(new WebJarLibrariesBuildItem(webJarLibraries)); | ||
} | ||
|
||
private WebJarLibrary createWebJarLibrary(ResolvedDependency dep, String webjarRootPath, | ||
Map<ArtifactKey, ClassPathElement> webJarKeys) { | ||
// If the dependency is not a runtime class path dependency, return null | ||
if (!dep.isRuntimeCp()) { | ||
return null; | ||
} | ||
final ClassPathElement provider = webJarKeys.get(dep.getKey()); | ||
if (provider == null) { | ||
return null; | ||
} | ||
final WebJarLibrary webJarLibrary = new WebJarLibrary(provider.getDependencyKey().getArtifactId()); | ||
provider.apply(tree -> { | ||
final Path webjarsDir = tree.getPath(WEBJARS_PREFIX); | ||
final Path nameDir; | ||
try (Stream<Path> webjarsDirPaths = Files.list(webjarsDir)) { | ||
nameDir = webjarsDirPaths.filter(Files::isDirectory).findFirst().orElseThrow(() -> new IOException( | ||
"Could not find name directory for " + dep.getKey().getArtifactId() + " in " + webjarsDir)); | ||
} catch (IOException e) { | ||
throw new UncheckedIOException(e); | ||
} | ||
final Path versionDir; | ||
Path root = nameDir; | ||
// The base URL for the webjar | ||
final StringBuilder urlBase = new StringBuilder(webjarRootPath); | ||
boolean appendRootPart = true; | ||
try { | ||
// If the version directory exists, use it as a root, otherwise use the name directory | ||
versionDir = nameDir.resolve(dep.getVersion()); | ||
root = Files.isDirectory(versionDir) ? versionDir : nameDir; | ||
urlBase.append(nameDir.getFileName().toString()) | ||
.append("/"); | ||
appendRootPart = false; | ||
} catch (InvalidPathException e) { | ||
log.warn("Could not find version directory for " + dep.getKey().getArtifactId() + " " | ||
+ dep.getVersion() + " in " + nameDir + ", falling back to name directory"); | ||
} | ||
webJarLibrary.setVersion(dep.getVersion()); | ||
try { | ||
// Create the asset tree for the webjar and set it as the root asset | ||
var asset = createAssetForLibrary(root, urlBase.toString(), appendRootPart); | ||
webJarLibrary.setRootAsset(asset); | ||
} catch (IOException e) { | ||
throw new UncheckedIOException(e); | ||
} | ||
|
||
return null; | ||
}); | ||
|
||
return webJarLibrary; | ||
} | ||
|
||
private WebJarAsset createAssetForLibrary(Path rootPath, String urlBase, boolean appendRootPart) | ||
throws IOException { | ||
//If it is a directory, go deeper, otherwise add the file | ||
var root = new WebJarAsset(); | ||
root.setName(rootPath.getFileName().toString()); | ||
root.setChildren(new LinkedList<>()); | ||
root.setFileAsset(false); | ||
urlBase = appendRootPart ? urlBase + root.getName() + "/" : urlBase; | ||
|
||
try (DirectoryStream<Path> directoryStream = Files.newDirectoryStream(rootPath)) { | ||
for (Path childPath : directoryStream) { | ||
if (Files.isDirectory(childPath)) { // If it is a directory, go deeper, otherwise add the file | ||
var childDir = createAssetForLibrary(childPath, urlBase, true); | ||
root.getChildren().add(childDir); | ||
} else { | ||
var childFile = new WebJarAsset(); | ||
childFile.setName(childPath.getFileName().toString()); | ||
childFile.setFileAsset(true); | ||
childFile.setUrlPart(urlBase + childFile.getName()); | ||
root.getChildren().add(childFile); | ||
} | ||
} | ||
} | ||
// Sort the children by name | ||
root.getChildren().sort(Comparator.comparing(WebJarAsset::getName)); | ||
return root; | ||
} | ||
|
||
} |
35 changes: 35 additions & 0 deletions
35
...src/main/java/io/quarkus/webjar/locator/deployment/devui/WebJarLocatorDevUIProcessor.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
package io.quarkus.webjar.locator.deployment.devui; | ||
|
||
import java.util.List; | ||
|
||
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; | ||
|
||
public class WebJarLocatorDevUIProcessor { | ||
|
||
@BuildStep(onlyIf = IsDevelopment.class) | ||
public void createPages(BuildProducer<CardPageBuildItem> cardPageProducer, | ||
WebJarLibrariesBuildItem webJarLibrariesBuildItem) { | ||
|
||
CardPageBuildItem cardPageBuildItem = new CardPageBuildItem(); | ||
List<WebJarLibrary> webJarLibraries = webJarLibrariesBuildItem.getWebJarLibraries(); | ||
|
||
if (!webJarLibraries.isEmpty()) { | ||
// WebJar Libraries | ||
cardPageBuildItem.addBuildTimeData("webJarLibraries", webJarLibraries); | ||
|
||
// WebJar Asset List | ||
cardPageBuildItem.addPage(Page.webComponentPageBuilder() | ||
.componentLink("qwc-webjar-locator-webjar-libraries.js") | ||
.title("WebJar Libraries") | ||
.icon("font-awesome-solid:folder-tree") | ||
.staticLabel(String.valueOf(webJarLibraries.size()))); | ||
} | ||
|
||
cardPageProducer.produce(cardPageBuildItem); | ||
} | ||
|
||
} |
104 changes: 104 additions & 0 deletions
104
...bjars-locator/deployment/src/main/resources/dev-ui/qwc-webjar-locator-webjar-libraries.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
import {LitElement, html, css} from 'lit'; | ||
import {webJarLibraries} from 'build-time-data'; | ||
import '@vaadin/tabsheet'; | ||
import '@vaadin/tabs'; | ||
import '@vaadin/grid'; | ||
import '@vaadin/icon'; | ||
import '@vaadin/button'; | ||
import '@vaadin/grid/vaadin-grid-tree-column.js'; | ||
import {notifier} from 'notifier'; | ||
import {columnBodyRenderer} from '@vaadin/grid/lit.js'; | ||
|
||
|
||
export class QwcWebjarLocatorWebjarLibraries extends LitElement { | ||
|
||
static styles = css` | ||
.full-height { | ||
height: 100%; | ||
} | ||
`; | ||
|
||
static properties = { | ||
_webJarLibraries: {}, | ||
}; | ||
|
||
constructor() { | ||
super(); | ||
this._webJarLibraries = webJarLibraries; | ||
} | ||
|
||
render() { | ||
return html` | ||
<vaadin-tabsheet class="full-height"> | ||
<vaadin-tabs slot="tabs"> | ||
${this._webJarLibraries.map(webjar => html` | ||
<vaadin-tab id="${webjar.webJarName}"> | ||
${webjar.webJarName + " (" + webjar.version + ")"} | ||
</vaadin-tab>`)} | ||
</vaadin-tabs> | ||
${this._webJarLibraries.map(webjar => this._renderLibraryAssets(webjar))} | ||
</vaadin-tabsheet> | ||
`; | ||
} | ||
|
||
_renderLibraryAssets(library) { | ||
const dataProvider = function (params, callback) { | ||
if (params.parentItem === undefined) { | ||
callback(library.rootAsset.children, library.rootAsset.children.length); | ||
} else { | ||
callback(params.parentItem.children, params.parentItem.children.length) | ||
} | ||
}; | ||
|
||
return html` | ||
<div tab="${library.webJarName}" class="full-height"> | ||
<vaadin-grid .itemHasChildrenPath="${'children'}" .dataProvider="${dataProvider}" | ||
theme="compact no-border" class="full-height"> | ||
<vaadin-grid-tree-column path="name"></vaadin-grid-tree-column> | ||
<vaadin-grid-column width="5em" header="Copy link" flex-grow="0" | ||
${columnBodyRenderer(this._assetCopyRenderer, [])}></vaadin-grid-column> | ||
<vaadin-grid-column width="6em" header="Open asset" flex-grow="0" | ||
${columnBodyRenderer(this._assetLinkRenderer, [])}></vaadin-grid-column> | ||
</vaadin-grid> | ||
</div>`; | ||
} | ||
|
||
_assetLinkRenderer(item) { | ||
if (item.fileAsset) { | ||
return html`<a href="${item.urlPart}" target="_blank" style="color: var(--lumo-contrast-80pct);"> | ||
<vaadin-icon style="font-size: small;" icon="font-awesome-solid:up-right-from-square" role="link" | ||
title="Open link to ${item.name} in a new tab"> | ||
</vaadin-icon> | ||
</a>`; | ||
} else { | ||
return html``; | ||
} | ||
} | ||
|
||
_assetCopyRenderer(item) { | ||
if (item.fileAsset) { | ||
return html` | ||
<vaadin-button theme="icon" style="margin: 0; height: 2em;" | ||
@click=${() => {this._onCopyLinkClick(item)}} | ||
aria-label="Copy link to ${item.name} to clipboard" | ||
title="Copy link to ${item.name} to clipboard"> | ||
<vaadin-icon style="font-size: small; cursor: pointer" icon="font-awesome-regular:copy" | ||
role="button"> | ||
</vaadin-icon> | ||
</vaadin-button>`; | ||
} else { | ||
return html``; | ||
} | ||
} | ||
|
||
_onCopyLinkClick(item) { | ||
navigator.clipboard.writeText(item.urlPart); | ||
notifier.showInfoMessage('URL for ' + item.name + ' copied to clipboard', 'top-end'); | ||
} | ||
|
||
} | ||
|
||
customElements.define('qwc-webjar-locator-webjar-libraries', QwcWebjarLocatorWebjarLibraries) |