Skip to content

Commit c5e6643

Browse files
authored
Merge pull request #49968 from mkouba/issue-49914
Introduce Template#getSource()
2 parents ea60ff6 + 5c6e1aa commit c5e6643

File tree

14 files changed

+358
-148
lines changed

14 files changed

+358
-148
lines changed

extensions/qute/deployment/src/main/java/io/quarkus/qute/deployment/QuteProcessor.java

Lines changed: 60 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import java.io.StringReader;
1414
import java.io.UncheckedIOException;
1515
import java.lang.reflect.Modifier;
16+
import java.net.URI;
1617
import java.nio.charset.Charset;
1718
import java.nio.file.Files;
1819
import java.nio.file.Path;
@@ -77,6 +78,9 @@
7778
import io.quarkus.arc.processor.DotNames;
7879
import io.quarkus.arc.processor.InjectionPointInfo;
7980
import io.quarkus.arc.processor.QualifierRegistrar;
81+
import io.quarkus.bootstrap.model.ApplicationModel;
82+
import io.quarkus.bootstrap.workspace.SourceDir;
83+
import io.quarkus.bootstrap.workspace.WorkspaceModule;
8084
import io.quarkus.deployment.ApplicationArchive;
8185
import io.quarkus.deployment.Feature;
8286
import io.quarkus.deployment.GeneratedClassGizmo2Adaptor;
@@ -91,12 +95,14 @@
9195
import io.quarkus.deployment.builditem.GeneratedClassBuildItem;
9296
import io.quarkus.deployment.builditem.GeneratedResourceBuildItem;
9397
import io.quarkus.deployment.builditem.HotDeploymentWatchedFileBuildItem;
98+
import io.quarkus.deployment.builditem.LaunchModeBuildItem;
9499
import io.quarkus.deployment.builditem.LiveReloadBuildItem;
95100
import io.quarkus.deployment.builditem.ServiceStartBuildItem;
96101
import io.quarkus.deployment.builditem.nativeimage.NativeImageResourceBuildItem;
97102
import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem;
98103
import io.quarkus.deployment.pkg.NativeConfig;
99104
import io.quarkus.deployment.pkg.builditem.CurateOutcomeBuildItem;
105+
import io.quarkus.dev.spi.DevModeType;
100106
import io.quarkus.gizmo2.ClassOutput;
101107
import io.quarkus.maven.dependency.ArtifactKey;
102108
import io.quarkus.maven.dependency.DependencyFlags;
@@ -148,6 +154,7 @@
148154
import io.quarkus.qute.runtime.QuteConfig;
149155
import io.quarkus.qute.runtime.QuteRecorder;
150156
import io.quarkus.qute.runtime.QuteRecorder.QuteContext;
157+
import io.quarkus.qute.runtime.QuteRecorder.TemplateInfo;
151158
import io.quarkus.qute.runtime.TemplateProducer;
152159
import io.quarkus.qute.runtime.debug.DebugQuteEngineObserver;
153160
import io.quarkus.qute.runtime.extensions.CollectionTemplateExtensions;
@@ -653,7 +660,7 @@ void analyzeTemplates(EffectiveTemplatePathsBuildItem effectiveTemplatePaths,
653660
for (TemplatePathBuildItem path : effectiveTemplatePaths.getTemplatePaths()) {
654661
if (path.isTag()) {
655662
String tagPath = path.getPath();
656-
String tagName = tagPath.substring(TemplatePathBuildItem.TAGS.length(), tagPath.length());
663+
String tagName = tagPath.substring(EngineProducer.TAGS.length(), tagPath.length());
657664
if (tagName.contains(".")) {
658665
tagName = tagName.substring(0, tagName.indexOf('.'));
659666
}
@@ -764,7 +771,7 @@ public void beforeParsing(ParserHelper parserHelper) {
764771
}
765772
}
766773

767-
if (templateId.startsWith(TemplatePathBuildItem.TAGS)) {
774+
if (templateId.startsWith(EngineProducer.TAGS)) {
768775
parserHelper.addParameter(UserTagSectionHelper.Factory.ARGS,
769776
UserTagSectionHelper.Arguments.class.getName());
770777
}
@@ -2222,7 +2229,8 @@ void collectTemplates(ApplicationArchivesBuildItem applicationArchives,
22222229
BuildProducer<TemplatePathBuildItem> templatePaths,
22232230
BuildProducer<NativeImageResourceBuildItem> nativeImageResources,
22242231
QuteConfig config,
2225-
TemplateRootsBuildItem templateRoots)
2232+
TemplateRootsBuildItem templateRoots,
2233+
LaunchModeBuildItem launchMode)
22262234
throws IOException {
22272235

22282236
// Make sure the new templates are watched as well
@@ -2244,55 +2252,82 @@ public boolean test(String path) {
22442252
excludePatterns.add(Pattern.compile(exclude.getRegexPattern()));
22452253
}
22462254

2255+
final boolean tryLocateSource = launchMode.getLaunchMode().isDev()
2256+
&& DevModeType.LOCAL == launchMode.getDevModeType().orElse(null);
22472257
final Set<ApplicationArchive> allApplicationArchives = applicationArchives.getAllApplicationArchives();
22482258
final Set<ArtifactKey> appArtifactKeys = new HashSet<>(allApplicationArchives.size());
22492259
for (var archive : allApplicationArchives) {
22502260
appArtifactKeys.add(archive.getKey());
22512261
}
2252-
for (ResolvedDependency artifact : curateOutcome.getApplicationModel()
2253-
.getDependencies(DependencyFlags.RUNTIME_EXTENSION_ARTIFACT)) {
2262+
ApplicationModel applicationModel = curateOutcome.getApplicationModel();
2263+
for (ResolvedDependency artifact : applicationModel.getDependencies(DependencyFlags.RUNTIME_EXTENSION_ARTIFACT)) {
22542264
// Skip extension archives that are also application archives
22552265
if (!appArtifactKeys.contains(artifact.getKey())) {
22562266
scanPathTree(artifact.getContentTree(), templateRoots, watchedPaths, templatePaths, nativeImageResources,
2257-
config, excludePatterns, TemplatePathBuildItem.APP_ARCHIVE_PRIORITY);
2267+
config, excludePatterns, TemplatePathBuildItem.APP_ARCHIVE_PRIORITY, null, tryLocateSource);
22582268
}
22592269
}
22602270
for (ApplicationArchive archive : applicationArchives.getApplicationArchives()) {
22612271
archive.accept(
22622272
tree -> scanPathTree(tree, templateRoots, watchedPaths, templatePaths, nativeImageResources, config,
2263-
excludePatterns, TemplatePathBuildItem.APP_ARCHIVE_PRIORITY));
2273+
excludePatterns, TemplatePathBuildItem.APP_ARCHIVE_PRIORITY, null, tryLocateSource));
2274+
}
2275+
WorkspaceModule appModule;
2276+
if (tryLocateSource) {
2277+
appModule = applicationModel.getApplicationModule();
2278+
} else {
2279+
appModule = null;
22642280
}
22652281
applicationArchives.getRootArchive().accept(
22662282
tree -> scanPathTree(tree, templateRoots, watchedPaths, templatePaths, nativeImageResources, config,
2267-
excludePatterns, TemplatePathBuildItem.ROOT_ARCHIVE_PRIORITY));
2283+
excludePatterns, TemplatePathBuildItem.ROOT_ARCHIVE_PRIORITY, appModule, tryLocateSource));
22682284
}
22692285

22702286
private void scanPathTree(PathTree pathTree, TemplateRootsBuildItem templateRoots,
22712287
BuildProducer<HotDeploymentWatchedFileBuildItem> watchedPaths,
22722288
BuildProducer<TemplatePathBuildItem> templatePaths,
22732289
BuildProducer<NativeImageResourceBuildItem> nativeImageResources,
22742290
QuteConfig config, List<Pattern> excludePatterns,
2275-
int templatePriority) {
2291+
int templatePriority, WorkspaceModule module, boolean tryLocateSource) {
22762292
for (String templateRoot : templateRoots) {
22772293
if (PathTreeUtils.containsCaseSensitivePath(pathTree, templateRoot)) {
22782294
pathTree.walkIfContains(templateRoot, visit -> {
2279-
if (Files.isRegularFile(visit.getPath())) {
2280-
if (!Identifiers.isValid(visit.getPath().getFileName().toString())) {
2295+
Path path = visit.getPath();
2296+
if (Files.isRegularFile(path)) {
2297+
if (!Identifiers.isValid(path.getFileName().toString())) {
22812298
LOGGER.warnf("Invalid file name detected [%s] - template is ignored", visit.getPath());
22822299
return;
22832300
}
2284-
LOGGER.debugf("Found template: %s", visit.getPath());
2301+
LOGGER.debugf("Found template: %s", path);
22852302
// remove templateRoot + /
22862303
final String relativePath = visit.getRelativePath();
22872304
String templatePath = relativePath.substring(templateRoot.length() + 1);
22882305
for (Pattern p : excludePatterns) {
22892306
if (p.matcher(templatePath).matches()) {
2290-
LOGGER.debugf("Template file excluded: %s", visit.getPath());
2307+
LOGGER.debugf("Template file excluded: %s", path);
22912308
return;
22922309
}
22932310
}
2311+
// Try to find source
2312+
URI source = null;
2313+
if (module != null) {
2314+
for (SourceDir resources : module.getMainSources().getResourceDirs()) {
2315+
Path sourcePath = resources.getDir().resolve(visit.getRelativePath());
2316+
if (Files.isRegularFile(sourcePath)) {
2317+
LOGGER.debugf("Source file found for template %s: %s", templatePath, sourcePath);
2318+
source = sourcePath.toUri();
2319+
}
2320+
}
2321+
} else if (tryLocateSource) {
2322+
try {
2323+
source = visit.getUrl().toURI();
2324+
LOGGER.debugf("Source file found for template %s: %s", templatePath, source);
2325+
} catch (Exception e) {
2326+
LOGGER.warnf("Unable to locate source for %s: %s", templatePath, e.toString());
2327+
}
2328+
}
22942329
produceTemplateBuildItems(templatePaths, watchedPaths, nativeImageResources,
2295-
relativePath, templatePath, visit.getPath(), config, templatePriority);
2330+
relativePath, templatePath, path, config, templatePriority, source);
22962331
}
22972332
});
22982333
}
@@ -2621,20 +2656,12 @@ void initialize(BuildProducer<SyntheticBeanBuildItem> syntheticBeans, QuteRecord
26212656
EffectiveTemplatePathsBuildItem effectiveTemplatePaths, Optional<TemplateVariantsBuildItem> templateVariants,
26222657
TemplateRootsBuildItem templateRoots, List<TemplatePathExcludeBuildItem> templatePathExcludes) {
26232658

2624-
List<String> templates = new ArrayList<>();
2625-
List<String> tags = new ArrayList<>();
2626-
Map<String, String> templateContents = new HashMap<>();
2627-
for (TemplatePathBuildItem templatePath : effectiveTemplatePaths.getTemplatePaths()) {
2628-
if (templatePath.isTag()) {
2629-
// tags/myTag.html -> myTag.html
2630-
String tagPath = templatePath.getPath();
2631-
tags.add(tagPath.substring(TemplatePathBuildItem.TAGS.length(), tagPath.length()));
2632-
} else {
2633-
templates.add(templatePath.getPath());
2634-
}
2635-
if (!templatePath.isFileBased()) {
2636-
templateContents.put(templatePath.getPath(), templatePath.getContent());
2637-
}
2659+
Map<String, TemplateInfo> templates = new HashMap<>();
2660+
for (TemplatePathBuildItem template : effectiveTemplatePaths.getTemplatePaths()) {
2661+
templates.put(template.getPath(),
2662+
new TemplateInfo(template.getPath(),
2663+
template.getSource() != null ? template.getSource().toString() : null,
2664+
template.isFileBased() ? null : template.getContent()));
26382665
}
26392666
Map<String, List<String>> variants;
26402667
if (templateVariants.isPresent()) {
@@ -2650,10 +2677,9 @@ void initialize(BuildProducer<SyntheticBeanBuildItem> syntheticBeans, QuteRecord
26502677

26512678
syntheticBeans.produce(SyntheticBeanBuildItem.configure(QuteContext.class)
26522679
.scope(BuiltinScope.SINGLETON.getInfo())
2653-
.supplier(recorder.createContext(templates,
2654-
tags, variants,
2655-
templateRoots.getPaths().stream().map(p -> p + "/").collect(Collectors.toSet()), templateContents,
2656-
excludePatterns))
2680+
.supplier(recorder.createContext(variants,
2681+
templateRoots.getPaths().stream().map(p -> p + "/").collect(Collectors.toSet()),
2682+
excludePatterns, templates))
26572683
.done());
26582684
}
26592685

@@ -3635,7 +3661,7 @@ public static String getName(InjectionPointInfo injectionPoint) {
36353661
private static void produceTemplateBuildItems(BuildProducer<TemplatePathBuildItem> templatePaths,
36363662
BuildProducer<HotDeploymentWatchedFileBuildItem> watchedPaths,
36373663
BuildProducer<NativeImageResourceBuildItem> nativeImageResources, String resourcePath,
3638-
String templatePath, Path originalPath, QuteConfig config, int templatePriority) {
3664+
String templatePath, Path originalPath, QuteConfig config, int templatePriority, URI source) {
36393665
if (templatePath.isEmpty()) {
36403666
return;
36413667
}
@@ -3652,6 +3678,7 @@ private static void produceTemplateBuildItems(BuildProducer<TemplatePathBuildIte
36523678
templatePaths.produce(TemplatePathBuildItem.builder()
36533679
.path(templatePath)
36543680
.fullPath(originalPath)
3681+
.source(source)
36553682
.priority(templatePriority)
36563683
.content(readTemplateContent(originalPath, config.defaultCharset()))
36573684
.build());

extensions/qute/deployment/src/main/java/io/quarkus/qute/deployment/TemplatePathBuildItem.java

Lines changed: 28 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
package io.quarkus.qute.deployment;
22

3+
import java.net.URI;
34
import java.nio.file.Path;
45
import java.util.Objects;
56

67
import io.quarkus.builder.item.MultiBuildItem;
8+
import io.quarkus.qute.runtime.EngineProducer;
79
import io.quarkus.qute.runtime.QuteConfig;
810

911
/**
@@ -44,34 +46,23 @@ public static Builder builder() {
4446
return new Builder();
4547
}
4648

47-
static final String TAGS = "tags/";
48-
4949
private final String path;
5050
private final String content;
5151
private final Path fullPath;
5252
private final String extensionInfo;
5353

5454
private final int priority;
5555

56-
/**
57-
*
58-
* @param path
59-
* @param fullPath
60-
* @param content
61-
* @deprecated Use the {@link #builder()} instead
62-
*/
63-
@Deprecated(forRemoval = true, since = "3.13")
64-
public TemplatePathBuildItem(String path, Path fullPath, String content) {
65-
this(Objects.requireNonNull(path), Objects.requireNonNull(content), Objects.requireNonNull(fullPath), null,
66-
BUILD_ITEM_PRIORITY);
67-
}
56+
private final URI source;
6857

69-
private TemplatePathBuildItem(String path, String content, Path fullPath, String extensionInfo, int priority) {
58+
private TemplatePathBuildItem(String path, String content, Path fullPath, String extensionInfo, int priority,
59+
URI source) {
7060
this.path = path;
7161
this.content = content;
7262
this.fullPath = fullPath;
7363
this.extensionInfo = extensionInfo;
7464
this.priority = priority;
65+
this.source = source;
7566
}
7667

7768
/**
@@ -95,6 +86,14 @@ public Path getFullPath() {
9586
return fullPath;
9687
}
9788

89+
/**
90+
*
91+
* @return the source, or {@code null} if not available
92+
*/
93+
public URI getSource() {
94+
return source;
95+
}
96+
9897
/**
9998
*
10099
* @return the content of the template
@@ -125,7 +124,7 @@ public int getPriority() {
125124
* @return {@code true} if it represents a user tag, {@code false} otherwise
126125
*/
127126
public boolean isTag() {
128-
return path.startsWith(TAGS);
127+
return path.startsWith(EngineProducer.TAGS);
129128
}
130129

131130
/**
@@ -153,6 +152,7 @@ public static class Builder {
153152
private String path;
154153
private String content;
155154
private Path fullPath;
155+
private URI source;
156156
private String extensionInfo;
157157
private int priority = BUILD_ITEM_PRIORITY;
158158

@@ -192,6 +192,17 @@ public Builder fullPath(Path fullPath) {
192192
return this;
193193
}
194194

195+
/**
196+
* Set the source path of the template.
197+
*
198+
* @param source
199+
* @return self
200+
*/
201+
public Builder source(URI source) {
202+
this.source = source;
203+
return this;
204+
}
205+
195206
/**
196207
* Set the extension info for templates that are not backed by a file.
197208
*
@@ -219,7 +230,7 @@ public TemplatePathBuildItem build() {
219230
if (fullPath == null && extensionInfo == null) {
220231
throw new IllegalStateException("Templates that are not backed by a file must provide extension info");
221232
}
222-
return new TemplatePathBuildItem(path, content, fullPath, extensionInfo, priority);
233+
return new TemplatePathBuildItem(path, content, fullPath, extensionInfo, priority, source);
223234
}
224235

225236
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package io.quarkus.qute.deployment.devmode;
2+
3+
import java.net.URI;
4+
5+
import jakarta.inject.Inject;
6+
7+
import io.quarkus.qute.Template;
8+
import io.quarkus.vertx.web.Route;
9+
import io.vertx.ext.web.RoutingContext;
10+
11+
public class SourceRoute {
12+
13+
@Inject
14+
Template test;
15+
16+
@Route(path = "test")
17+
public void test(RoutingContext ctx) {
18+
URI source = test.getSource().orElse(null);
19+
if (source == null) {
20+
ctx.response().setStatusCode(500).end();
21+
} else {
22+
ctx.end(source.toString());
23+
}
24+
}
25+
26+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package io.quarkus.qute.deployment.devmode;
2+
3+
import static io.restassured.RestAssured.given;
4+
5+
import org.hamcrest.Matchers;
6+
import org.jboss.shrinkwrap.api.asset.StringAsset;
7+
import org.junit.jupiter.api.Test;
8+
import org.junit.jupiter.api.extension.RegisterExtension;
9+
10+
import io.quarkus.test.QuarkusDevModeTest;
11+
12+
/**
13+
* Test that Template#getSource() works correctly in the local dev mode.
14+
*/
15+
public class TemplateSourceDevModeTest {
16+
17+
@RegisterExtension
18+
static final QuarkusDevModeTest config = new QuarkusDevModeTest()
19+
.withApplicationRoot(root -> root
20+
.addClasses(SourceRoute.class)
21+
.addAsResource(new StringAsset(
22+
"{foo}"),
23+
"templates/test.html"));
24+
25+
@Test
26+
public void testTemplateSource() {
27+
given().get("test")
28+
.then()
29+
.statusCode(200)
30+
.body(Matchers.startsWith("file:"),
31+
Matchers.endsWith("src/main/resources/templates/test.html"));
32+
}
33+
34+
}

0 commit comments

Comments
 (0)