Compare commits

...

33 Commits
v0.1.2 ... main

Author SHA1 Message Date
Jesse Brault
3202e51d7f Update TODO.md. 2025-02-16 11:15:25 -06:00
Jesse Brault
abc4b971d6 Remove unused projects: cli, groowt-all, groowt-gradle, groowt-gradle-model. 2025-02-16 11:09:46 -06:00
Jesse Brault
eb77f0a3d7 Remove groowt all publishing.
All checks were successful
Groowt Check and Publish / ci (push) Successful in 5m38s
2025-02-16 10:27:12 -06:00
Jesse Brault
71f1547c6a Update archive base names and artifact ids. 2025-02-16 10:26:00 -06:00
Jesse Brault
1b63f2807c Set action to only run when versioned tag is pushed. 2025-02-16 10:02:37 -06:00
Jesse Brault
0c4b42e0ee Rename action and setup individual publications.
All checks were successful
Groowt Check and Publish / ci (push) Successful in 5m35s
2025-02-16 09:59:21 -06:00
Jesse Brault
eb444dda02 Remove setup gradle as it is very slow.
Some checks failed
Groowt CI Pipeline / ci (push) Failing after 5m35s
2025-02-16 09:41:56 -06:00
Jesse Brault
dcc1d6e53e Added publishing to packages task.
Some checks are pending
Groowt CI Pipeline / ci (push) Waiting to run
2025-02-15 21:29:40 -06:00
Jesse Brault
1d0de99e1d Add ubuntu-latest back in. 2025-02-15 21:11:23 -06:00
Jesse Brault
b4ef7d7da3 Using setup-java and setup-gradle.
Some checks failed
Groowt CI Pipeline / ci (push) Has been cancelled
2025-02-15 21:10:21 -06:00
Jesse Brault
21e933d681 Using Eclipse-Temurin 21 docker image.
Some checks failed
Groowt CI Pipeline / ci (push) Has been cancelled
2025-02-15 21:07:07 -06:00
Jesse Brault
7105686f80 Bump to v0.1.3. Add Gitea CI pipeline.
Some checks failed
Groowt CI Pipeline / ci (push) Failing after 4s
2025-02-15 21:04:26 -06:00
Jesse Brault
0390d15fce Update Gradle to 8.12.1 and dependency updates. Tests passing. 2025-02-15 20:44:17 -06:00
Jesse Brault
9495849dc9 Update TODO with finished tasks for 0.1.3. 2025-01-27 14:38:26 -06:00
Jesse Brault
f6071909b6 Fix broken OutletTests, found and fixed bug with ancestor searching. 2025-01-27 14:37:35 -06:00
Jesse Brault
2b3cd3120c Add OutletContainer and check to renderer of Outlet. 2025-01-27 14:18:49 -06:00
Jesse Brault
369dc51779 Added hasAncestor and findNearestAncestor(Class) methods to ComponentContext. 2025-01-27 14:16:00 -06:00
Jesse Brault
148ced050b Move WithHtml to lib package. 2025-01-27 14:08:57 -06:00
Jesse Brault
589bbca889 Update TODO with more 0.2.0 goals. 2025-01-27 07:01:55 -06:00
Jesse Brault
2b935da385 Update TODO with fixed wvcc bug. 2025-01-26 15:42:30 -06:00
Jesse Brault
7a28b0530d Add auto imports to GroovyWvcCompiler. 2025-01-26 15:40:11 -06:00
Jesse Brault
45d188d064 Auto-imports working for both Groovyc configuration and independent API. 2025-01-26 15:35:18 -06:00
Jesse Brault
bd4dee98fa Move auto-imports to an ImportCustomizer defined in DefaultWebViewComponentTemplateCompileUnit. 2025-01-26 14:46:54 -06:00
Jesse Brault
1a528465f9 Remove pre-emptive resolving mechanism, relying instead on built-in Groovyc resolution. 2025-01-26 13:23:31 -06:00
Jesse Brault
5ce934b3fe Make logging clearer in ClassLoaderComponentClassNodeResolver. 2025-01-25 14:05:55 -06:00
Jesse Brault
74c5698d1b Add nested static view classes bug to TODO.md. 2025-01-25 00:45:02 -06:00
Jesse Brault
525932668f Fix bug where @Singleton classes bound toSelf() caused stack overflow. 2025-01-25 00:39:37 -06:00
Jesse Brault
04866b4d3a Merge remote-tracking branch 'origin/main' 2025-01-24 15:07:43 -06:00
Jesse Brault
8bc3d9d793 Fix binTemplate.gst to use VERSION variable with current groowt version. 2025-01-24 15:05:31 -06:00
JesseBrault0709
55adf223d2 Merge remote-tracking branch 'origin/main' 2024-07-28 16:46:32 -05:00
Jesse Brault
22b929225f Updated to Gradle 8.9 and jbarchiva 0.2.2. 2024-07-17 15:02:25 -05:00
JesseBrault0709
49ead642c4 Added a todo. 2024-06-17 14:32:58 +02:00
JesseBrault0709
b55cea174d Added todos. 2024-06-14 12:55:10 +02:00
76 changed files with 364 additions and 1320 deletions

View File

@ -0,0 +1,36 @@
name: Groowt Check and Publish
on:
push:
tags:
- v*
jobs:
ci:
runs-on: ubuntu-latest
steps:
- name: Checkout the code
uses: actions/checkout@v4
- name: Setup Java.
uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: 21
- name: Check libraries
run: ./gradlew check
- name: Publish to git.jessebrault.com
run: >
./gradlew publishViewsPublicationToGiteaRepository &&
./gradlew publishViewComponentsPublicationToGiteaRepository &&
./gradlew publishWebViewComponentsPublicationToGiteaRepository &&
./gradlew publishWebViewComponentsCompilerPublicationToGiteaRepository &&
./gradlew publishDiPublicationToGiteaRepository &&
./gradlew publishExtensiblePublicationToGiteaRepository &&
./gradlew publishFpPublicationToGiteaRepository
- name: Publish to archiva.jessebrault.com
run: >
./gradlew publishViewsPublicationToJbArchivaInternalRepository &&
./gradlew publishViewComponentsPublicationToJbArchivaInternalRepository &&
./gradlew publishWebViewComponentsPublicationToJbArchivaInternalRepository &&
./gradlew publishWebViewComponentsCompilerPublicationToJbArchivaInternalRepository &&
./gradlew publishDiPublicationToJbArchivaInternalRepository &&
./gradlew publishExtensiblePublicationToJbArchivaInternalRepository &&
./gradlew publishFpPublicationToJbArchivaInternalRepository

19
TODO.md
View File

@ -20,12 +20,23 @@ For example:
- di - di
- extensible - extensible
- fp - fp
- [ ] Remove gradle plugins and whatnot until we actually build the whole framework - [x] Remove cli, groowt-all, groowt-gradle, groowt-gradle-model.
- [ ] Get rid of wvc compiler dependency on fp, di.
- [ ] Remove as much cruft as possible from web-view-components-compiler, etc.
- [ ] Use new namespaces (i.e., packages) the individual projects:
- `com.jessebrault.groowt`: for views
- `com.jessebrault.groowt.component`: for view-components
- `com.jessebrault.groowt.web`: for web-view-components and web-view-components-compiler
## 0.1.3 ## 0.1.3
- [ ] refactor tools/gradle start scripts to use dist instead of custom bin script - [ ] ~~refactor tools/gradle start scripts to use dist instead of custom bin script~~
- [ ] have custom bin/* scripts which point to dist(s) for convenience - [ ] ~~have custom bin/* scripts which point to dist(s) for convenience~~
- [ ] di bug: @Singleton toSelf() causes stack overflow - [x] di bug: @Singleton toSelf() causes stack overflow
- [x] wvcc bug: Nested static view classes are not seen by compiler
- This required tweaking how the configurations are passed around. Ultimately, we should strive for less complexity
in this regard.
- [x] `OutletContainer` trait or interface for components which can contain an `<Outlet />` child.
- [x] `Context` should have methods for simply finding an ancestor of a certain type without the need for a predicate.
## 0.1.2 ## 0.1.2
- [x] `Outlet` component for rendering children like so: - [x] `Outlet` component for rendering children like so:

View File

@ -4,7 +4,7 @@ plugins {
} }
group = 'groowt' group = 'groowt'
version = '0.1.2' version = '0.1.3'
repositories { repositories {
mavenCentral() mavenCentral()

View File

@ -4,6 +4,8 @@ import com.jessebrault.jbarchiva.JbArchivaPlugin
import org.gradle.api.Plugin import org.gradle.api.Plugin
import org.gradle.api.Project import org.gradle.api.Project
import org.gradle.api.publish.maven.plugins.MavenPublishPlugin import org.gradle.api.publish.maven.plugins.MavenPublishPlugin
import org.gradle.api.credentials.HttpHeaderCredentials
import org.gradle.authentication.http.HttpHeaderAuthentication
class GroowtPublishPlugin implements Plugin<Project> { class GroowtPublishPlugin implements Plugin<Project> {
@ -11,6 +13,25 @@ class GroowtPublishPlugin implements Plugin<Project> {
void apply(Project project) { void apply(Project project) {
project.plugins.apply(MavenPublishPlugin) project.plugins.apply(MavenPublishPlugin)
project.plugins.apply(JbArchivaPlugin) project.plugins.apply(JbArchivaPlugin)
project.with {
publishing {
repositories {
maven {
name = "Gitea"
url = uri("https://git.jessebrault.com/api/packages/jessebrault/maven")
credentials(HttpHeaderCredentials) {
name = "Authorization"
value = "token ${System.getenv("GITEA_ACCESS_TOKEN")}"
}
authentication {
header(HttpHeaderAuthentication)
}
}
}
}
}
} }
} }

View File

@ -1,46 +0,0 @@
plugins {
id 'groowt-conventions'
id 'groowt-logging'
id 'groowt-publish'
}
repositories {
maven {
url 'https://repo.gradle.org/gradle/libs-releases'
}
}
dependencies {
implementation libs.gradle.tooling
implementation libs.picocli
implementation project(':groowt-gradle-model')
}
tasks.named('jar', Jar) {
manifest {
attributes('Main-Class': 'groowt.cli.GroowtCli')
}
from sourceSets.main.runtimeClasspath.filter(File.&exists).collect { it.isDirectory() ? it : zipTree(it) }
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
dependsOn ':groowt-gradle-model:jar'
}
tasks.withType(GenerateModuleMetadata).configureEach {
enabled = false
}
publishing {
publications {
create('groowtCli', MavenPublication) {
artifactId = 'groowt-cli'
from components.java
pom {
withXml {
def rootNode = asNode()
def dependenciesNode = rootNode.get('dependencies')
rootNode.remove(dependenciesNode)
}
}
}
}
}

View File

@ -1,17 +0,0 @@
package groowt.cli;
import java.io.File;
public final class FileAndPathUtil {
public static File packageNameToFile(String packageName) {
return new File(packageName.replace(".", File.separator));
}
public static File resolve(File from, File to) {
return from.toPath().resolve(to.toPath()).toFile();
}
private FileAndPathUtil() {}
}

View File

@ -1,61 +0,0 @@
package groowt.cli;
import picocli.CommandLine;
import picocli.CommandLine.Command;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.concurrent.Callable;
@Command(name = "generate", aliases = "gen", description = "Generate a component, template, model, etc.")
public final class Generate implements Callable<Integer> {
@CommandLine.ParentCommand
private GroowtCli cli;
@CommandLine.Option(
names = { "-c", "--component" },
description = "Create a component with the given name."
)
private String componentName;
@CommandLine.Option(
names = { "-s", "--sourceSet" },
description = "The source set in which to generate the component, etc.",
defaultValue = "main"
)
private String sourceSet;
@CommandLine.Option(
names = { "-d", "--srcDir", "--sourceDir", "--sourceDirectory" },
description = "The directory in the source set in which to generate the component, etc."
)
private File sourceDir;
@SuppressWarnings("ResultOfMethodCallIgnored")
@Override
public Integer call() {
if (this.componentName != null) {
GradleUtil.doWithGroowtGradleModel(this.cli.getProjectDir(), model -> {
if (this.sourceDir == null) {
this.sourceDir = new File(String.join(File.separator, "src", this.sourceSet, "groovy"));
}
final File packageDir = FileAndPathUtil.resolve(
this.sourceDir,
FileAndPathUtil.packageNameToFile(model.getBasePackage())
);
packageDir.mkdirs();
final File componentFile = new File(packageDir, this.componentName + ".txt");
try (final OutputStream componentFileOutputStream = new FileOutputStream(componentFile)) {
componentFileOutputStream.write("Hello, Groowt!".getBytes());
} catch (IOException e) {
throw new RuntimeException(e);
}
});
}
return 0;
}
}

View File

@ -1,32 +0,0 @@
package groowt.cli;
import groowt.gradle.model.GroowtGradleModel;
import org.gradle.tooling.GradleConnector;
import org.gradle.tooling.ProjectConnection;
import java.io.File;
import java.util.function.Consumer;
public final class GradleUtil {
public static void doWith(File projectDir, Consumer<? super ProjectConnection> action) {
final var gradleConnector = GradleConnector.newConnector().forProjectDirectory(projectDir);
try (final var projectConnection = gradleConnector.connect()) {
action.accept(projectConnection);
}
}
public static <T> void doWith(File projectDir, Class<? extends T> modelClass, Consumer<? super T> modelConsumer) {
doWith(projectDir, projectConnection -> {
final T model = projectConnection.getModel(modelClass);
modelConsumer.accept(model);
});
}
public static void doWithGroowtGradleModel(File projectDir, Consumer<? super GroowtGradleModel> modelConsumer) {
doWith(projectDir, GroowtGradleModel.class, modelConsumer);
}
private GradleUtil() {}
}

View File

@ -1,46 +0,0 @@
package groowt.cli;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import picocli.CommandLine;
import java.io.File;
@CommandLine.Command(
name = "groowt",
description = "The command line interface facilitating development of a Groowt project.",
mixinStandardHelpOptions = true,
version = "0.1.0",
subcommands = { Generate.class }
)
public final class GroowtCli {
private static final Logger logger = LoggerFactory.getLogger(GroowtCli.class);
@CommandLine.Option(
names = { "-v", "--verbose" },
description = "Log verbosely to standard out."
)
private boolean verbose;
@CommandLine.Option(
names = { "--projectDir" },
defaultValue = ".",
description = "The root directory of the groowt project."
)
private File projectDir;
public static void main(String[] args) {
logger.info("Hello from Groowt! Version 0.1.0");
System.exit(new CommandLine(new GroowtCli()).execute(args));
}
public boolean isVerbose() {
return this.verbose;
}
public File getProjectDir() {
return this.projectDir;
}
}

View File

@ -1,17 +0,0 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Configuration xmlns="http://logging.apache.org/log4j/2.0/config">
<Appenders>
<Console name="root">
<PatternLayout>
<LevelPatternSelector defaultPattern="[%t] %-5level %logger{1.} %msg%n">
<PatternMatch key="DEBUG" pattern="[%t] %-5level %logger{1.}.%M() %msg%n"/>
</LevelPatternSelector>
</PatternLayout>
</Console>
</Appenders>
<Loggers>
<Root level="INFO">
<AppenderRef ref="root"/>
</Root>
</Loggers>
</Configuration>

View File

@ -2,34 +2,31 @@
# https://docs.gradle.org/current/userguide/platforms.html#sub::toml-dependencies-format # https://docs.gradle.org/current/userguide/platforms.html#sub::toml-dependencies-format
[versions] [versions]
antlr = '4.13.1' antlr = '4.13.2'
groovy = '4.0.21' groovy = '4.0.25'
junit = '5.10.2' junit = '5.11.4'
kotlin = '1.9.23' kotlin = '1.9.25'
log4j = '2.23.1' log4j = '2.24.3'
mockito = '5.11.0' mockito = '5.15.2'
slf4j = '2.0.12' slf4j = '2.0.16'
[libraries] [libraries]
antlr = { module = 'org.antlr:antlr4', version.ref = 'antlr' } antlr = { module = 'org.antlr:antlr4', version.ref = 'antlr' }
antlr-runtime = { module = 'org.antlr:antlr4-runtime', version.ref = 'antlr' } antlr-runtime = { module = 'org.antlr:antlr4-runtime', version.ref = 'antlr' }
asm = 'org.ow2.asm:asm:9.7' asm = 'org.ow2.asm:asm:9.7.1'
classgraph = 'io.github.classgraph:classgraph:4.8.172' gradle-tooling = 'org.gradle:gradle-tooling-api:8.12.1'
gradle-tooling = 'org.gradle:gradle-tooling-api:8.6'
groovy = { module = 'org.apache.groovy:groovy', version.ref = 'groovy' } groovy = { module = 'org.apache.groovy:groovy', version.ref = 'groovy' }
groovy-all = { module = 'org.apache.groovy:groovy-all', version.ref = 'groovy' }
groovy-console = { module = 'org.apache.groovy:groovy-console', version.ref = 'groovy' } groovy-console = { module = 'org.apache.groovy:groovy-console', version.ref = 'groovy' }
groovy-templates = { module = 'org.apache.groovy:groovy-templates', version.ref = 'groovy' } groovy-templates = { module = 'org.apache.groovy:groovy-templates', version.ref = 'groovy' }
jakarta-inject = 'jakarta.inject:jakarta.inject-api:2.0.1' jakarta-inject = 'jakarta.inject:jakarta.inject-api:2.0.1'
jansi = 'org.fusesource.jansi:jansi:2.4.1' jansi = 'org.fusesource.jansi:jansi:2.4.1'
jbarchiva = 'com.jessebrault.jbarchiva:jbarchiva:0.2.1' jbarchiva = 'com.jessebrault.jbarchiva:jbarchiva:0.2.2'
jetbrains-anotations = 'org.jetbrains:annotations:24.1.0' jetbrains-anotations = 'org.jetbrains:annotations:26.0.2'
junit-jupiter-api = { module = 'org.junit.jupiter:junit-jupiter-api', version.ref = 'junit' } junit-jupiter-api = { module = 'org.junit.jupiter:junit-jupiter-api', version.ref = 'junit' }
kotlin-stdlib = { module = 'org.jetbrains.kotlin:kotlin-stdlib', version.ref = 'kotlin' } kotlin-stdlib = { module = 'org.jetbrains.kotlin:kotlin-stdlib', version.ref = 'kotlin' }
kotlin-test = { module = 'org.jetbrains.kotlin:kotlin-test', version.ref = 'kotlin' }
log4j-core = { module = 'org.apache.logging.log4j:log4j-core', version.ref = 'log4j' } log4j-core = { module = 'org.apache.logging.log4j:log4j-core', version.ref = 'log4j' }
log4j-slf4jBinding = { module = 'org.apache.logging.log4j:log4j-slf4j2-impl', version.ref = 'log4j' } log4j-slf4jBinding = { module = 'org.apache.logging.log4j:log4j-slf4j2-impl', version.ref = 'log4j' }
mockito-core = { module = 'org.mockito:mockito-core', version.ref = 'mockito' } mockito-core = { module = 'org.mockito:mockito-core', version.ref = 'mockito' }
mockito-junit = { module = 'org.mockito:mockito-junit-jupiter', version.ref = 'mockito' } mockito-junit = { module = 'org.mockito:mockito-junit-jupiter', version.ref = 'mockito' }
picocli = 'info.picocli:picocli:4.7.5' picocli = 'info.picocli:picocli:4.7.6'
slf4j-api = { module = 'org.slf4j:slf4j-api', version.ref = 'slf4j' } slf4j-api = { module = 'org.slf4j:slf4j-api', version.ref = 'slf4j' }

Binary file not shown.

View File

@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-8.12.1-bin.zip
networkTimeout=10000 networkTimeout=10000
validateDistributionUrl=true validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME

6
gradlew vendored
View File

@ -15,6 +15,8 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
# #
# SPDX-License-Identifier: Apache-2.0
#
############################################################################## ##############################################################################
# #
@ -55,7 +57,7 @@
# Darwin, MinGW, and NonStop. # Darwin, MinGW, and NonStop.
# #
# (3) This script is generated from the Groovy template # (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project. # within the Gradle project.
# #
# You can find Gradle at https://github.com/gradle/gradle/. # You can find Gradle at https://github.com/gradle/gradle/.
@ -84,7 +86,7 @@ done
# shellcheck disable=SC2034 # shellcheck disable=SC2034
APP_BASE_NAME=${0##*/} APP_BASE_NAME=${0##*/}
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value. # Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum MAX_FD=maximum

2
gradlew.bat vendored
View File

@ -13,6 +13,8 @@
@rem See the License for the specific language governing permissions and @rem See the License for the specific language governing permissions and
@rem limitations under the License. @rem limitations under the License.
@rem @rem
@rem SPDX-License-Identifier: Apache-2.0
@rem
@if "%DEBUG%"=="" @echo off @if "%DEBUG%"=="" @echo off
@rem ########################################################################## @rem ##########################################################################

View File

@ -1,23 +0,0 @@
plugins {
id 'groowt-conventions'
id 'groowt-publish'
id 'java-library'
}
dependencies {
api project(':views')
api project(':view-components')
api project(':web-view-components')
api project(':di')
api project(':extensible')
api project(':fp')
}
publishing {
publications {
create('groowtAll', MavenPublication) {
artifactId = 'groowt-all'
from components.java
}
}
}

View File

@ -1,14 +0,0 @@
plugins {
id 'groowt-conventions'
id 'java-library'
id 'groowt-publish'
}
publishing {
publications {
create('groowtGradleModel', MavenPublication) {
artifactId = 'groowt-gradle-model'
from components.java
}
}
}

View File

@ -1,32 +0,0 @@
package groowt.gradle.model;
import java.io.File;
import java.io.Serializable;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
public class DefaultGroowtGradleModel implements GroowtGradleModel, Serializable {
private String basePackage;
private Map<String, Set<File>> sourceSetToTemplatesDirs;
@Override
public String getBasePackage() {
return this.basePackage;
}
public void setBasePackage(String basePackage) {
this.basePackage = Objects.requireNonNull(basePackage);
}
@Override
public Map<String, Set<File>> getSourceSetToTemplatesDirs() {
return Objects.requireNonNull(this.sourceSetToTemplatesDirs);
}
public void setSourceFileSets(Map<String, Set<File>> sourceSetToTemplateDir) {
this.sourceSetToTemplatesDirs = sourceSetToTemplateDir;
}
}

View File

@ -1,10 +0,0 @@
package groowt.gradle.model;
import java.io.File;
import java.util.Map;
import java.util.Set;
public interface GroowtGradleModel {
String getBasePackage();
Map<String, Set<File>> getSourceSetToTemplatesDirs();
}

View File

@ -1,32 +0,0 @@
plugins {
id 'groowt-conventions'
id 'java-gradle-plugin'
id 'groowt-publish'
}
repositories {
mavenCentral()
}
dependencies {
implementation libs.groovy
implementation project(':groowt-gradle-model')
}
gradlePlugin {
plugins {
create('groowtGradle') {
id = 'groowt-gradle'
implementationClass = 'groowt.gradle.GroowtGradlePlugin'
}
}
}
publishing {
publications {
create('groowtGradlePlugin', MavenPublication) {
artifactId = 'groowt-gradle'
from components.java
}
}
}

View File

@ -1,22 +0,0 @@
package groowt.gradle;
import org.gradle.api.model.ObjectFactory;
import org.gradle.api.provider.Property;
import javax.inject.Inject;
public class DefaultGroowtExtension implements GroowtExtension {
private final Property<String> basePackage;
@Inject
public DefaultGroowtExtension(ObjectFactory objectFactory) {
this.basePackage = objectFactory.property(String.class);
}
@Override
public Property<String> getBasePackage() {
return this.basePackage;
}
}

View File

@ -1,19 +0,0 @@
package groowt.gradle;
import org.gradle.api.file.SourceDirectorySet;
import org.gradle.api.internal.file.DefaultSourceDirectorySet;
import org.gradle.api.internal.tasks.TaskDependencyFactory;
import javax.inject.Inject;
public class DefaultTemplateSourceDirectorySet extends DefaultSourceDirectorySet implements TemplateSourceDirectorySet {
@Inject
public DefaultTemplateSourceDirectorySet(
SourceDirectorySet delegate,
TaskDependencyFactory taskDependencyFactory
) {
super(delegate, taskDependencyFactory);
}
}

View File

@ -1,61 +0,0 @@
package groowt.gradle;
import groovy.lang.Closure;
import org.gradle.api.Action;
import org.gradle.api.file.SourceDirectorySet;
import org.gradle.api.model.ObjectFactory;
import org.gradle.api.reflect.HasPublicType;
import org.gradle.api.reflect.TypeOf;
import org.gradle.util.internal.ConfigureUtil;
import javax.inject.Inject;
public class DefaultTemplateSourceSet implements TemplateSourceSet, HasPublicType {
private final TemplateSourceDirectorySet templateSourceDirectorySet;
private final SourceDirectorySet allTemplates;
@Inject
public DefaultTemplateSourceSet(ObjectFactory objectFactory, String name, String displayName) {
this.templateSourceDirectorySet = objectFactory.newInstance(
DefaultTemplateSourceDirectorySet.class,
objectFactory.sourceDirectorySet(name, displayName + " ComponentTemplate sources")
);
this.templateSourceDirectorySet.getFilter().include("**/*.wvc", "**/*.gst");
this.allTemplates = objectFactory.sourceDirectorySet(
"all" + name,
displayName + " ComponentTemplate sources"
);
this.allTemplates.source(this.templateSourceDirectorySet);
this.allTemplates.getFilter().include("**/*.wvc", "**/*.gst");
}
@Override
public TypeOf<?> getPublicType() {
return TypeOf.typeOf(TemplateSourceSet.class);
}
@Override
public TemplateSourceDirectorySet getTemplates() {
return this.templateSourceDirectorySet;
}
@Override
public TemplateSourceSet templates(Action<? super TemplateSourceDirectorySet> action) {
action.execute(this.templateSourceDirectorySet);
return this;
}
@SuppressWarnings("rawtypes")
@Override
public TemplateSourceSet templates(Closure closure) {
ConfigureUtil.configure(closure, this.templateSourceDirectorySet);
return this;
}
@Override
public SourceDirectorySet getAllTemplates() {
return this.allTemplates;
}
}

View File

@ -1,7 +0,0 @@
package groowt.gradle;
import org.gradle.api.provider.Property;
public interface GroowtExtension {
Property<String> getBasePackage();
}

View File

@ -1,91 +0,0 @@
package groowt.gradle;
import groowt.gradle.model.GroowtGradleModelBuilder;
import org.gradle.api.Plugin;
import org.gradle.api.Project;
import org.gradle.api.artifacts.Configuration;
import org.gradle.api.file.FileCollection;
import org.gradle.api.internal.tasks.DefaultSourceSet;
import org.gradle.api.plugins.GroovyPlugin;
import org.gradle.api.plugins.JavaPlugin;
import org.gradle.api.plugins.JavaPluginExtension;
import org.gradle.api.provider.Provider;
import org.gradle.api.tasks.SourceSetContainer;
import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry;
import javax.inject.Inject;
import static org.gradle.api.internal.lambdas.SerializableLambdas.spec;
public class GroowtGradlePlugin implements Plugin<Project> {
private final ToolingModelBuilderRegistry modelBuilderRegistry;
@Inject
public GroowtGradlePlugin(ToolingModelBuilderRegistry modelBuilderRegistry) {
this.modelBuilderRegistry = modelBuilderRegistry;
}
@Override
public void apply(Project project) {
// Apply java and groovy plugins, if not done already
final var pluginManager = project.getPluginManager();
pluginManager.apply(JavaPlugin.class);
pluginManager.apply(GroovyPlugin.class);
// Create our groowt configuration for storing the groowt dependencies
final Provider<Configuration> groowtConfigurationProvider = project.getConfigurations()
.register("groowt", configuration -> {
configuration.setCanBeConsumed(false);
configuration.setCanBeResolved(true);
});
// Create groowt extension and source sets.
final GroowtExtension groowtExtension = project.getExtensions().create(
GroowtExtension.class,
"groowt",
DefaultGroowtExtension.class
);
groowtExtension.getBasePackage().convention("");
final JavaPluginExtension javaExtension = project.getExtensions().getByType(JavaPluginExtension.class);
final SourceSetContainer javaSourceSets = javaExtension.getSourceSets();
// data resources, such as texts, json files, sqlite-databases, etc.
javaSourceSets.getByName("main", mainSourceSet -> {
mainSourceSet.getResources().srcDir("src/data");
});
// TODO: figure out how we can set the compile behavior for all of these.
javaSourceSets.forEach(sourceSet -> {
final TemplateSourceSet templateSourceSet = project.getObjects().newInstance(
DefaultTemplateSourceSet.class,
"wvc",
((DefaultSourceSet) sourceSet).getDisplayName()
);
final TemplateSourceDirectorySet templateSourceDirectorySet = templateSourceSet.getTemplates();
sourceSet.getExtensions().add(
TemplateSourceDirectorySet.class,
"templates",
templateSourceDirectorySet
);
templateSourceDirectorySet.srcDir("src/" + sourceSet.getName() + "/templates");
// Explicitly capture only a FileCollection in the lambda below for compatibility with configuration-cache.
@SuppressWarnings("UnnecessaryLocalVariable")
final FileCollection templateSourceFiles = templateSourceDirectorySet;
sourceSet.getResources().getFilter().exclude(
spec(element -> templateSourceFiles.contains(element.getFile()))
);
sourceSet.getAllJava().source(templateSourceDirectorySet);
sourceSet.getAllSource().source(templateSourceDirectorySet);
});
// create init task
project.getTasks().create("groowtInit", GroowtInitTask.class, groowtConfigurationProvider);
// tooling models for cli
this.modelBuilderRegistry.register(new GroowtGradleModelBuilder());
}
}

View File

@ -1,95 +0,0 @@
package groowt.gradle;
import org.gradle.api.DefaultTask;
import org.gradle.api.Project;
import org.gradle.api.artifacts.Configuration;
import org.gradle.api.plugins.JavaPluginExtension;
import org.gradle.api.provider.Provider;
import org.gradle.api.tasks.TaskAction;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import java.io.*;
import java.util.Set;
public class GroowtInitTask extends DefaultTask {
private static final Logger logger = LoggerFactory.getLogger(GroowtInitTask.class);
private static final Set<String> srcDirsToMake = Set.of("groovy", "templates");
private final Provider<Configuration> groowtConfigurationProvider;
private final File binDir;
private final File groowtDir;
@Inject
public GroowtInitTask(Project project, Provider<Configuration> groowtConfigurationProvider) {
this.groowtConfigurationProvider = groowtConfigurationProvider;
this.binDir = new File(project.getRootDir(), "bin");
this.groowtDir = new File(project.getRootDir(), "groowt");
}
protected void createBin() throws IOException {
//noinspection ResultOfMethodCallIgnored
this.binDir.mkdirs();
final var groowtFile = new File(this.binDir, "groowt");
try (final InputStream bootstrapInputStream = this.getClass().getResourceAsStream("groowt")) {
if (bootstrapInputStream != null) {
try (final OutputStream bootstrapOutputStream = new FileOutputStream(groowtFile)) {
bootstrapInputStream.transferTo(bootstrapOutputStream);
}
if (!groowtFile.setExecutable(true)) {
logger.warn("Could not set bin/groowt to executable; you will have to do this yourself.");
}
} else {
throw new RuntimeException("Could not find groowt shell script.");
}
}
}
protected void createGroowtFolder() throws IOException {
//noinspection ResultOfMethodCallIgnored
this.groowtDir.mkdirs();
final var groowtConfiguration = this.groowtConfigurationProvider.get();
final Set<File> groowtCliFiles = groowtConfiguration.files(dependency -> {
final var group = dependency.getGroup();
if (group == null || !group.equals("groowt")) {
return false;
} else {
return dependency.getName().equals("groowt-cli");
}
});
final File groowtCliJarFile = groowtCliFiles.stream()
.filter(file -> file.getName().endsWith(".jar"))
.findFirst()
.orElseThrow(() -> new RuntimeException("Could not find groowt-cli jar file."));
final File groowtCliJarOutputFile = new File(this.groowtDir, "groowt-cli.jar");
try (final InputStream groowtCliJarInputStream = new FileInputStream(groowtCliJarFile)) {
try (final OutputStream groowtCliJarOutputStream = new FileOutputStream(groowtCliJarOutputFile)) {
groowtCliJarInputStream.transferTo(groowtCliJarOutputStream);
}
}
}
protected void createSrcDirs() {
final var javaPluginExtension = this.getProject().getExtensions().getByType(JavaPluginExtension.class);
javaPluginExtension.getSourceSets().forEach(sourceSet -> {
final var srcDirs = sourceSet.getAllSource().getSrcDirs();
srcDirs.forEach(srcDir -> {
if (!sourceSet.getName().contains("test") && srcDirsToMake.contains(srcDir.getName())) {
//noinspection ResultOfMethodCallIgnored
srcDir.mkdirs();
}
});
});
}
@TaskAction
public void doInit() throws IOException {
this.createBin();
this.createGroowtFolder();
this.createSrcDirs();
}
}

View File

@ -1,5 +0,0 @@
package groowt.gradle;
import org.gradle.api.file.SourceDirectorySet;
public interface TemplateSourceDirectorySet extends SourceDirectorySet {}

View File

@ -1,18 +0,0 @@
package groowt.gradle;
import groovy.lang.Closure;
import org.gradle.api.Action;
import org.gradle.api.file.SourceDirectorySet;
public interface TemplateSourceSet {
TemplateSourceDirectorySet getTemplates();
SourceDirectorySet getAllTemplates();
TemplateSourceSet templates(Action<? super TemplateSourceDirectorySet> action);
@SuppressWarnings("rawtypes")
TemplateSourceSet templates(Closure closure);
}

View File

@ -1,50 +0,0 @@
package groowt.gradle.model;
import groowt.gradle.GroowtExtension;
import groowt.gradle.TemplateSourceDirectorySet;
import org.gradle.api.Project;
import org.gradle.api.plugins.JavaPluginExtension;
import org.gradle.api.provider.Property;
import org.gradle.tooling.provider.model.ToolingModelBuilder;
import org.jetbrains.annotations.NotNull;
import java.io.File;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class GroowtGradleModelBuilder implements ToolingModelBuilder {
@Override
public boolean canBuild(String modelName) {
return modelName.equals(GroowtGradleModel.class.getName());
}
@Override
public @NotNull Object buildAll(@NotNull String modelName, Project project) {
final DefaultGroowtGradleModel model = new DefaultGroowtGradleModel();
final var groowtExtension = project.getExtensions().getByType(GroowtExtension.class);
// base package
final Property<String> basePackage = groowtExtension.getBasePackage();
if (!basePackage.isPresent()) {
throw new RuntimeException(
"The property 'basePackage' must be set under the 'groowt' extension in build.gradle"
);
}
model.setBasePackage(basePackage.get());
// templates dirs
final Map<String, Set<File>> sourceSetToTemplatesDirs = new HashMap<>();
final var javaExtension = project.getExtensions().getByType(JavaPluginExtension.class);
javaExtension.getSourceSets().forEach(sourceSet -> {
final TemplateSourceDirectorySet templateSourceDirectorySet =
sourceSet.getExtensions().getByType(TemplateSourceDirectorySet.class);
sourceSetToTemplatesDirs.put(sourceSet.getName(), templateSourceDirectorySet.getFiles());
});
model.setSourceFileSets(sourceSetToTemplatesDirs);
return model;
}
}

View File

@ -1,3 +0,0 @@
#!/usr/bin/env bash
java -jar groowt/groowt-cli.jar "$@"

View File

@ -6,8 +6,7 @@ pluginManagement {
rootProject.name = 'groowt' rootProject.name = 'groowt'
include 'cli', 'groowt-all', 'groowt-gradle', 'groowt-gradle-model', 'views', 'view-components', include 'views', 'view-components', 'web-view-components', 'web-view-components-compiler'
'web-view-components', 'web-view-components-compiler'
file('util').eachDir { file('util').eachDir {
include it.name include it.name

View File

@ -24,7 +24,7 @@ jar {
publishing { publishing {
publications { publications {
create('di', MavenPublication) { create('di', MavenPublication) {
artifactId = 'groowt-util-di' artifactId = 'util-di'
from components.java from components.java
} }
} }

View File

@ -3,31 +3,58 @@ package groowt.util.di;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import java.lang.annotation.Annotation; import java.lang.annotation.Annotation;
import java.util.ArrayList; import java.util.*;
import java.util.Collection;
import java.util.List;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.function.Function; import java.util.function.Function;
import java.util.function.Predicate; import java.util.function.Predicate;
public class DefaultRegistry implements Registry { public class DefaultRegistry implements Registry {
protected record ClassKeyBinding<T>(Class<T> key, Binding<T> binding) {} protected static class BindingContainer {
protected final Collection<ClassKeyBinding<?>> classBindings = new ArrayList<>(); private final Map<Class<?>, Binding<?>> bindings = new HashMap<>();
@SuppressWarnings("unchecked")
public <T> @Nullable Binding<T> get(Class<T> key) {
for (final var entry : bindings.entrySet()) {
if (entry.getKey().isAssignableFrom(key)) {
return (Binding<T>) entry.getValue();
}
}
return null;
}
public <T> void put(Class<T> key, Binding<T> binding) {
this.bindings.put(key, binding);
}
public void remove(Class<?> key) {
this.bindings.remove(key);
}
public <T> void removeIf(Class<T> key, Predicate<? super Binding<T>> filter) {
if (filter.test(this.get(key))) {
this.bindings.remove(key);
}
}
public void clear() {
this.bindings.clear();
}
}
protected final BindingContainer bindingContainer = new BindingContainer();
protected final Collection<RegistryExtension> extensions = new ArrayList<>(); protected final Collection<RegistryExtension> extensions = new ArrayList<>();
@Override @Override
public void removeBinding(Class<?> key) { public void removeBinding(Class<?> key) {
this.classBindings.removeIf(classKeyBinding -> classKeyBinding.key().equals(key)); this.bindingContainer.remove(key);
} }
@SuppressWarnings("unchecked")
@Override @Override
public <T> void removeBindingIf(Class<T> key, Predicate<Binding<T>> filter) { public <T> void removeBindingIf(Class<T> key, Predicate<Binding<T>> filter) {
this.classBindings.removeIf(classKeyBinding -> this.bindingContainer.removeIf(key, filter);
classKeyBinding.key().equals(key) && filter.test((Binding<T>) classKeyBinding.binding())
);
} }
private <E extends RegistryExtension> List<E> getAllRegistryExtensions(Class<E> extensionType) { private <E extends RegistryExtension> List<E> getAllRegistryExtensions(Class<E> extensionType) {
@ -117,18 +144,12 @@ public class DefaultRegistry implements Registry {
public <T> void bind(Class<T> key, Consumer<? super BindingConfigurator<T>> configure) { public <T> void bind(Class<T> key, Consumer<? super BindingConfigurator<T>> configure) {
final var configurator = new SimpleBindingConfigurator<>(key); final var configurator = new SimpleBindingConfigurator<>(key);
configure.accept(configurator); configure.accept(configurator);
this.classBindings.add(new ClassKeyBinding<>(key, configurator.getBinding())); this.bindingContainer.put(key, configurator.getBinding());
} }
@SuppressWarnings("unchecked")
@Override @Override
public @Nullable <T> Binding<T> getBinding(Class<T> key) { public <T> @Nullable Binding<T> getBinding(Class<T> key) {
for (final var classKeyBinding : this.classBindings) { return this.bindingContainer.get(key);
if (key.isAssignableFrom(classKeyBinding.key())) {
return (Binding<T>) classKeyBinding.binding();
}
}
return null;
} }
private KeyBinder<?> findKeyBinder(Class<?> keyClass) { private KeyBinder<?> findKeyBinder(Class<?> keyClass) {
@ -189,7 +210,7 @@ public class DefaultRegistry implements Registry {
@Override @Override
public void clearAllBindings() { public void clearAllBindings() {
this.classBindings.clear(); this.bindingContainer.clear();
for (final var extension : this.extensions) { for (final var extension : this.extensions) {
if (extension instanceof KeyBinder<?> keyBinder) { if (extension instanceof KeyBinder<?> keyBinder) {
keyBinder.clearAllBindings(); keyBinder.clearAllBindings();

View File

@ -1,8 +1,9 @@
package groowt.util.di; package groowt.util.di;
import jakarta.inject.Provider;
import jakarta.inject.Singleton; import jakarta.inject.Singleton;
import static groowt.util.di.BindingUtil.toSingleton; import static groowt.util.di.BindingUtil.toLazySingleton;
public final class SingletonScopeHandler implements ScopeHandler<Singleton> { public final class SingletonScopeHandler implements ScopeHandler<Singleton> {
@ -19,12 +20,22 @@ public final class SingletonScopeHandler implements ScopeHandler<Singleton> {
RegistryObjectFactory objectFactory RegistryObjectFactory objectFactory
) { ) {
final Binding<T> potentialBinding = this.owner.getBinding(dependencyClass); final Binding<T> potentialBinding = this.owner.getBinding(dependencyClass);
if (potentialBinding != null) { return switch (potentialBinding) {
return potentialBinding; case ClassBinding<T>(Class<T> from, Class<? extends T> to) -> {
} else { this.owner.bind(from, toLazySingleton(() -> objectFactory.createInstance(to)));
this.owner.bind(dependencyClass, toSingleton(objectFactory.createInstance(dependencyClass))); yield this.owner.getBinding(from);
return this.owner.getBinding(dependencyClass); }
} case ProviderBinding<T>(Class<T> from, Provider<? extends T> provider) -> {
this.owner.bind(from, toLazySingleton(provider::get));
yield this.owner.getBinding(from);
}
case SingletonBinding<T> singletonBinding -> singletonBinding;
case LazySingletonBinding<T> lazySingletonBinding -> lazySingletonBinding;
case null -> {
this.owner.bind(dependencyClass, toLazySingleton(() -> objectFactory.createInstance(dependencyClass)));
yield this.owner.getBinding(dependencyClass);
}
};
} }
@Override @Override

View File

@ -2,6 +2,7 @@ package groowt.util.di;
import jakarta.inject.Inject; import jakarta.inject.Inject;
import jakarta.inject.Named; import jakarta.inject.Named;
import jakarta.inject.Singleton;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import static groowt.util.di.BindingUtil.*; import static groowt.util.di.BindingUtil.*;
@ -250,4 +251,25 @@ public class DefaultRegistryObjectFactoryTests {
assertEquals("Given Greeting", g.greet()); assertEquals("Given Greeting", g.greet());
} }
@Singleton
public static final class SingletonGreeter implements Greeter {
@Override
public String greet() {
return "Hello, World!";
}
}
@Test
public void singletonDoesNotOverflow() {
final var b = DefaultRegistryObjectFactory.Builder.withDefaults();
b.configureRegistry(r -> {
r.bind(SingletonGreeter.class, toSelf());
});
final var f = b.build();
final var g = f.get(SingletonGreeter.class);
assertEquals("Hello, World!", g.greet());
}
} }

View File

@ -19,7 +19,7 @@ jar {
publishing { publishing {
publications { publications {
create('extensible', MavenPublication) { create('extensible', MavenPublication) {
artifactId = 'groowt-util-extensible' artifactId = 'util-extensible'
from components.java from components.java
} }
} }

View File

@ -24,7 +24,7 @@ jar {
publishing { publishing {
publications { publications {
create('fp', MavenPublication) { create('fp', MavenPublication) {
artifactId = 'groowt-util-fp' artifactId = 'util-fp'
from components.java from components.java
} }
} }

View File

@ -22,10 +22,14 @@ java {
withSourcesJar() withSourcesJar()
} }
jar {
archiveBaseName = 'groowt-view-components'
}
publishing { publishing {
publications { publications {
create('viewComponents', MavenPublication) { create('viewComponents', MavenPublication) {
artifactId = 'groowt-view-components' artifactId = 'view-components'
from components.java from components.java
} }
} }

View File

@ -10,41 +10,8 @@ public abstract class CachingComponentTemplateCompiler<U extends ComponentTempla
private final Map<Class<? extends ViewComponent>, ComponentTemplateCompileResult> cache = new HashMap<>(); private final Map<Class<? extends ViewComponent>, ComponentTemplateCompileResult> cache = new HashMap<>();
// private ComponentTemplate instantiate(
// GroovyClassLoader groovyClassLoader,
// CompileResult compileResult
// ) {
// for (final var groovyClass : compileResult.otherClasses()) {
// // Try to find it. If we can't, we need to load it via the groovy loader
// try {
// Class.forName(groovyClass.getName(), true, groovyClassLoader);
// } catch (ClassNotFoundException ignored) {
// groovyClassLoader.defineClass(groovyClass.getName(), groovyClass.getBytes());
// } catch (LinkageError ignored) {
// // no-op, because we already have it
// }
// }
// final GroovyClass templateGroovyClass = compileResult.templateClass();
// Class<?> templateClass;
// // Try to find it. If we can't, we need to load it via the groovy loader
// try {
// templateClass = Class.forName(templateGroovyClass.getName(), true, groovyClassLoader);
// } catch (ClassNotFoundException ignored) {
// templateClass = groovyClassLoader.defineClass(
// templateGroovyClass.getName(),
// templateGroovyClass.getBytes()
// );
// }
// try {
// return (ComponentTemplate) templateClass.getConstructor().newInstance();
// } catch (Exception e) {
// throw new RuntimeException("Unable to instantiate ComponentTemplate " + templateClass.getName(), e);
// }
// }
@Override @Override
public final ComponentTemplateCompileResult compile(U compileUnit) public final ComponentTemplateCompileResult compile(U compileUnit) throws ComponentTemplateCompileException {
throws ComponentTemplateCompileException {
if (this.cache.containsKey(compileUnit.getForClass())) { if (this.cache.containsKey(compileUnit.getForClass())) {
return this.cache.get(compileUnit.getForClass()); return this.cache.get(compileUnit.getForClass());
} else { } else {

View File

@ -4,16 +4,9 @@ import groowt.view.component.ViewComponent;
import groowt.view.component.compiler.source.ComponentTemplateSource; import groowt.view.component.compiler.source.ComponentTemplateSource;
public interface ComponentTemplateCompileUnit { public interface ComponentTemplateCompileUnit {
String getDescriptiveName(); String getDescriptiveName();
Class<? extends ViewComponent> getForClass(); Class<? extends ViewComponent> getForClass();
String getDefaultPackageName(); String getDefaultPackageName();
ComponentTemplateSource getSource(); ComponentTemplateSource getSource();
ComponentTemplateCompileResult compile(ComponentTemplateCompilerConfiguration configuration) ComponentTemplateCompileResult compile() throws ComponentTemplateCompileException;
throws ComponentTemplateCompileException;
default ComponentTemplateCompileResult compile() throws ComponentTemplateCompileException {
return this.compile(new DefaultComponentTemplateCompilerConfiguration());
}
} }

View File

@ -1,11 +0,0 @@
package groowt.view.component.compiler;
import groovy.lang.GroovyClassLoader;
import org.codehaus.groovy.control.CompilePhase;
import org.codehaus.groovy.control.CompilerConfiguration;
public interface ComponentTemplateCompilerConfiguration {
GroovyClassLoader getGroovyClassLoader();
CompilerConfiguration getGroovyCompilerConfiguration();
CompilePhase getToCompilePhase();
}

View File

@ -1,48 +0,0 @@
package groowt.view.component.compiler;
import groovy.lang.GroovyClassLoader;
import org.codehaus.groovy.control.CompilePhase;
import org.codehaus.groovy.control.CompilerConfiguration;
import static java.util.Objects.requireNonNull;
public class DefaultComponentTemplateCompilerConfiguration implements ComponentTemplateCompilerConfiguration {
private GroovyClassLoader groovyClassLoader;
private CompilerConfiguration groovyCompilerConfiguration;
private CompilePhase toCompilePhase;
public DefaultComponentTemplateCompilerConfiguration() {
this.groovyClassLoader = new GroovyClassLoader(Thread.currentThread().getContextClassLoader());
this.groovyCompilerConfiguration = new CompilerConfiguration();
this.toCompilePhase = CompilePhase.CLASS_GENERATION;
}
@Override
public GroovyClassLoader getGroovyClassLoader() {
return this.groovyClassLoader;
}
public void setGroovyClassLoader(GroovyClassLoader groovyClassLoader) {
this.groovyClassLoader = requireNonNull(groovyClassLoader);
}
@Override
public CompilerConfiguration getGroovyCompilerConfiguration() {
return this.groovyCompilerConfiguration;
}
public void setGroovyCompilerConfiguration(CompilerConfiguration groovyCompilerConfiguration) {
this.groovyCompilerConfiguration = requireNonNull(groovyCompilerConfiguration);
}
@Override
public CompilePhase getToCompilePhase() {
return this.toCompilePhase;
}
public void setToCompilePhase(CompilePhase toCompilePhase) {
this.toCompilePhase = requireNonNull(toCompilePhase);
}
}

View File

@ -99,6 +99,23 @@ public interface ComponentContext {
return ancestorClass.cast(this.findNearestAncestor(matching.and(ancestorClass::isInstance))); return ancestorClass.cast(this.findNearestAncestor(matching.and(ancestorClass::isInstance)));
} }
default <T extends ViewComponent> @Nullable T findNearestAncestor(Class<T> ancestorClass) {
return ancestorClass.cast(this.findNearestAncestor(ancestorClass::isInstance));
}
boolean hasAncestor(Predicate<? super ViewComponent> matching);
default <T extends ViewComponent> boolean hasAncestor(
Class<T> ancestorClass,
Predicate<? super ViewComponent> matching
) {
return this.hasAncestor(matching.and(ancestorClass::isInstance));
}
default <T extends ViewComponent> boolean hasAncestor(Class<T> ancestorClass) {
return this.hasAncestor(ancestorClass::isInstance);
}
List<ViewComponent> getAllAncestors(); List<ViewComponent> getAllAncestors();
} }

View File

@ -70,7 +70,10 @@ public class DefaultComponentContext implements ComponentContext {
public @Nullable ViewComponent findNearestAncestor(Predicate<? super ViewComponent> matching) { public @Nullable ViewComponent findNearestAncestor(Predicate<? super ViewComponent> matching) {
final List<ViewComponent> componentStack = this.getRenderContext().getComponentStack(); final List<ViewComponent> componentStack = this.getRenderContext().getComponentStack();
if (componentStack.size() > 1) { if (componentStack.size() > 1) {
for (final var ancestor : componentStack.subList(1, componentStack.size() -1)) { // 1/27/25: earlier this was originally componentStack.size() - 1 as the second argument to sublist().
// On examination today, it didn't make sense, because it would be chopping off the farthest ancestor from
// search. So I removed it, in accordance with the implementation in hasAncestor().
for (final var ancestor : componentStack.subList(1, componentStack.size())) {
if (matching.test(ancestor)) { if (matching.test(ancestor)) {
return ancestor; return ancestor;
} }
@ -79,6 +82,19 @@ public class DefaultComponentContext implements ComponentContext {
return null; return null;
} }
@Override
public boolean hasAncestor(Predicate<? super ViewComponent> matching) {
final List<ViewComponent> componentStack = this.getRenderContext().getComponentStack();
if (componentStack.size() > 1) {
for (final var ancestor : componentStack.subList(1, componentStack.size())) {
if (matching.test(ancestor)) {
return true;
}
}
}
return false;
}
@Override @Override
public List<ViewComponent> getAllAncestors() { public List<ViewComponent> getAllAncestors() {
final List<ViewComponent> componentStack = this.getRenderContext().getComponentStack(); final List<ViewComponent> componentStack = this.getRenderContext().getComponentStack();

View File

@ -25,10 +25,14 @@ java {
withSourcesJar() withSourcesJar()
} }
jar {
archiveBaseName = 'groowt-views'
}
publishing { publishing {
publications { publications {
create('views', MavenPublication) { create('views', MavenPublication) {
artifactId = 'groowt-views' artifactId = 'views'
from components.java from components.java
} }
} }

View File

@ -199,7 +199,9 @@ tasks.register('uberJar', Jar) {
group 'groovyc' group 'groovyc'
archiveBaseName = 'web-view-components-uber' archiveBaseName = 'web-view-components-uber'
from sourceSets.main.output from sourceSets.main.output
from sourceSets.main.runtimeClasspath.filter(File.&exists).collect { it.isDirectory() ? it : zipTree(it) } from sourceSets.main.runtimeClasspath
.filter(File.&exists)
.collect { it.isDirectory() ? it : zipTree(it) }
duplicatesStrategy = DuplicatesStrategy.EXCLUDE duplicatesStrategy = DuplicatesStrategy.EXCLUDE
} }
@ -217,6 +219,10 @@ tasks.named('sourcesJar', Jar) {
dependsOn 'generateAllAntlr' dependsOn 'generateAllAntlr'
} }
jar {
archiveBaseName = 'groowt-web-view-components-compiler'
}
publishing { publishing {
publications { publications {
create('webViewComponentsCompiler', MavenPublication) { create('webViewComponentsCompiler', MavenPublication) {

View File

@ -4,14 +4,15 @@ if [ "$1" == "--debug" ]; then
shift shift
gradle -q uberJar && \ gradle -q uberJar && \
java -agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=*:8192 \ java -agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=*:8192 \
-cp build/libs/web-view-components-uber-0.1.0.jar \ -cp build/libs/web-view-components-uber-0.1.2.jar \
org.codehaus.groovy.tools.FileSystemCompiler \ org.codehaus.groovy.tools.FileSystemCompiler \
--configscript src/main/resources/groowt/view/component/web/groovyc/groovycConfigurationScript.groovy \ --configscript src/main/resources/groowt/view/component/web/groovyc/groovycConfigurationScript.groovy \
-d groovyc-out \ -d groovyc-out \
"$@" "$@"
else else
gradle -q uberJar && \ gradle -q uberJar && \
groovyc -cp build/libs/web-view-components-uber-0.1.0.jar \ java -cp build/libs/web-view-components-uber-0.1.2.jar \
org.codehaus.groovy.tools.FileSystemCompiler \
--configscript src/main/resources/groowt/view/component/web/groovyc/groovycConfigurationScript.groovy \ --configscript src/main/resources/groowt/view/component/web/groovyc/groovycConfigurationScript.groovy \
-d groovyc-out \ -d groovyc-out \
"$@" "$@"

View File

@ -0,0 +1 @@
<Echo>Hello, World!</Echo>

View File

@ -4,7 +4,6 @@ import groowt.view.component.compiler.*;
import groowt.view.component.web.WebViewComponentBugError; import groowt.view.component.web.WebViewComponentBugError;
import groowt.view.component.web.ast.node.CompilationUnitNode; import groowt.view.component.web.ast.node.CompilationUnitNode;
import groowt.view.component.web.transpile.DefaultGroovyTranspiler; import groowt.view.component.web.transpile.DefaultGroovyTranspiler;
import org.antlr.v4.runtime.ParserRuleContext;
import org.codehaus.groovy.control.CompilationFailedException; import org.codehaus.groovy.control.CompilationFailedException;
import org.codehaus.groovy.control.SourceUnit; import org.codehaus.groovy.control.SourceUnit;
import org.codehaus.groovy.tools.GroovyClass; import org.codehaus.groovy.tools.GroovyClass;
@ -16,25 +15,12 @@ public class DefaultWebViewComponentTemplateCompiler
extends CachingComponentTemplateCompiler<WebViewComponentTemplateCompileUnit> extends CachingComponentTemplateCompiler<WebViewComponentTemplateCompileUnit>
implements WebViewComponentTemplateCompiler { implements WebViewComponentTemplateCompiler {
private final ComponentTemplateCompilerConfiguration configuration; private final WebViewComponentTemplateCompilerConfiguration configuration;
public DefaultWebViewComponentTemplateCompiler(ComponentTemplateCompilerConfiguration configuration) { public DefaultWebViewComponentTemplateCompiler(WebViewComponentTemplateCompilerConfiguration configuration) {
this.configuration = configuration; this.configuration = configuration;
} }
protected WebViewComponentTemplateCompileException getException(
WebViewComponentTemplateCompileUnit compileUnit,
ParserRuleContext parserRuleContext
) {
final var exception = new WebViewComponentTemplateCompileException(
compileUnit,
"Parser error: " + parserRuleContext.exception.getMessage(),
parserRuleContext.exception
);
exception.setParserRuleContext(parserRuleContext);
return exception;
}
@Override @Override
protected ComponentTemplateCompileResult doCompile(WebViewComponentTemplateCompileUnit compileUnit) protected ComponentTemplateCompileResult doCompile(WebViewComponentTemplateCompileUnit compileUnit)
throws ComponentTemplateCompileException { throws ComponentTemplateCompileException {
@ -49,22 +35,12 @@ public class DefaultWebViewComponentTemplateCompiler
: "AnonymousWebViewComponent" + System.nanoTime(); : "AnonymousWebViewComponent" + System.nanoTime();
final var templateClassSimpleName = ownerComponentName + "Template"; final var templateClassSimpleName = ownerComponentName + "Template";
final SourceUnit sourceUnit = transpiler.transpile( final SourceUnit sourceUnit = transpiler.transpile(compileUnit, cuNode, templateClassSimpleName);
this.configuration,
compileUnit,
cuNode,
templateClassSimpleName
);
compileUnit.getGroovyCompilationUnit().addSource(sourceUnit); compileUnit.getGroovyCompilationUnit().addSource(sourceUnit);
// set the groovy compile unit's class loader to the configuration's classloader.
compileUnit.getGroovyCompilationUnit().setClassLoader(
this.configuration.getGroovyClassLoader()
);
// compile groovy // compile groovy
try { try {
compileUnit.getGroovyCompilationUnit().compile(this.configuration.getToCompilePhase().getPhaseNumber()); compileUnit.getGroovyCompilationUnit().compile(this.configuration.getCompilePhase().getPhaseNumber());
} catch (CompilationFailedException compilationFailedException) { } catch (CompilationFailedException compilationFailedException) {
throw new WebViewComponentTemplateCompileException( throw new WebViewComponentTemplateCompileException(
compileUnit, compileUnit,

View File

@ -1,11 +1,9 @@
package groowt.view.component.web.compiler; package groowt.view.component.web.compiler;
import groowt.view.component.compiler.ComponentTemplateCompilerConfiguration;
public class DefaultWebViewComponentTemplateCompilerFactory implements WebViewComponentTemplateCompilerFactory { public class DefaultWebViewComponentTemplateCompilerFactory implements WebViewComponentTemplateCompilerFactory {
@Override @Override
public WebViewComponentTemplateCompiler create(ComponentTemplateCompilerConfiguration configuration) { public WebViewComponentTemplateCompiler create(WebViewComponentTemplateCompilerConfiguration configuration) {
return new DefaultWebViewComponentTemplateCompiler(configuration); return new DefaultWebViewComponentTemplateCompiler(configuration);
} }

View File

@ -1,7 +1,6 @@
package groowt.view.component.web.groovyc; package groowt.view.component.web.groovyc;
import groowt.view.component.compiler.ComponentTemplateCompileException; import groowt.view.component.compiler.ComponentTemplateCompileException;
import groowt.view.component.compiler.DefaultComponentTemplateCompilerConfiguration;
import groowt.view.component.compiler.source.ComponentTemplateSource; import groowt.view.component.compiler.source.ComponentTemplateSource;
import groowt.view.component.web.WebViewComponentBugError; import groowt.view.component.web.WebViewComponentBugError;
import groowt.view.component.web.ast.node.CompilationUnitNode; import groowt.view.component.web.ast.node.CompilationUnitNode;
@ -80,13 +79,12 @@ public class DelegatingWebViewComponentTemplateParserPlugin implements ParserPlu
} }
final var groovyTranspiler = new DefaultGroovyTranspiler(); final var groovyTranspiler = new DefaultGroovyTranspiler();
final String teplateClassSimpleName = sourceUnitFileName.substring(0, sourceUnitFileName.length() - 4); final String templateClassName = sourceUnitFileName.substring(0, sourceUnitFileName.length() - 4);
try { try {
final SourceUnit transpiledSourceUnit = groovyTranspiler.transpile( final SourceUnit transpiledSourceUnit = groovyTranspiler.transpile(
new DefaultComponentTemplateCompilerConfiguration(),
compileUnit, compileUnit,
cuNode, cuNode,
teplateClassSimpleName templateClassName
); );
return transpiledSourceUnit.getAST(); return transpiledSourceUnit.getAST();
} catch (ComponentTemplateCompileException e) { } catch (ComponentTemplateCompileException e) {

View File

@ -6,7 +6,6 @@ import groowt.view.component.web.WebViewComponentBugError;
import groowt.view.component.web.ast.node.*; import groowt.view.component.web.ast.node.*;
import groowt.view.component.web.transpile.groovy.GroovyUtil; import groowt.view.component.web.transpile.groovy.GroovyUtil;
import groowt.view.component.web.transpile.groovy.GroovyUtil.ConvertResult; import groowt.view.component.web.transpile.groovy.GroovyUtil.ConvertResult;
import groowt.view.component.web.transpile.resolve.ComponentClassNodeResolver;
import groowt.view.component.web.util.SourcePosition; import groowt.view.component.web.util.SourcePosition;
import org.codehaus.groovy.ast.*; import org.codehaus.groovy.ast.*;
import org.codehaus.groovy.ast.expr.*; import org.codehaus.groovy.ast.expr.*;
@ -15,7 +14,6 @@ import org.jetbrains.annotations.Nullable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.regex.Pattern;
import static groowt.view.component.web.transpile.TranspilerUtil.*; import static groowt.view.component.web.transpile.TranspilerUtil.*;
@ -25,13 +23,9 @@ public class DefaultComponentTranspiler implements ComponentTranspiler {
private static final ClassNode COMPONENT_RESOLVE_EXCEPTION_TYPE = ClassHelper.make(ComponentResolveException.class); private static final ClassNode COMPONENT_RESOLVE_EXCEPTION_TYPE = ClassHelper.make(ComponentResolveException.class);
private static final ClassNode COMPONENT_CREATE_EXCEPTION_TYPE = ClassHelper.make(ComponentCreateException.class); private static final ClassNode COMPONENT_CREATE_EXCEPTION_TYPE = ClassHelper.make(ComponentCreateException.class);
private static final Pattern isFqn = Pattern.compile("^(\\p{Ll}.+\\.)+\\p{Lu}.+$");
private static final Pattern isWithPackage = Pattern.compile("^\\p{Ll}.+\\.");
private LeftShiftFactory leftShiftFactory; private LeftShiftFactory leftShiftFactory;
private ValueNodeTranspiler valueNodeTranspiler; private ValueNodeTranspiler valueNodeTranspiler;
private BodyTranspiler bodyTranspiler; private BodyTranspiler bodyTranspiler;
private ComponentClassNodeResolver componentClassNodeResolver;
public void setLeftShiftFactory(LeftShiftFactory leftShiftFactory) { public void setLeftShiftFactory(LeftShiftFactory leftShiftFactory) {
this.leftShiftFactory = leftShiftFactory; this.leftShiftFactory = leftShiftFactory;
@ -45,10 +39,6 @@ public class DefaultComponentTranspiler implements ComponentTranspiler {
this.bodyTranspiler = bodyTranspiler; this.bodyTranspiler = bodyTranspiler;
} }
public void setComponentClassNodeResolver(ComponentClassNodeResolver componentClassNodeResolver) {
this.componentClassNodeResolver = componentClassNodeResolver;
}
/* UTIL */ /* UTIL */
protected String getComponentName(int componentNumber) { protected String getComponentName(int componentNumber) {
@ -74,49 +64,14 @@ public class DefaultComponentTranspiler implements ComponentTranspiler {
/* RESOLVE */ /* RESOLVE */
protected List<Expression> getArgsAsList( protected List<Expression> getArgsAsList(TypedComponentNode componentNode) {
TypedComponentNode componentNode,
TranspilerState state
) {
return switch (componentNode.getArgs().getType()) { return switch (componentNode.getArgs().getType()) {
case ClassComponentTypeNode classComponentTypeNode -> { case ClassComponentTypeNode classComponentTypeNode -> {
final String identifier = classComponentTypeNode.getIdentifier(); final String identifier = classComponentTypeNode.getIdentifier();
final ConstantExpression alias = getStringLiteral(identifier); final ConstantExpression alias = getStringLiteral(identifier);
final var matcher = isFqn.matcher(identifier); final ClassNode classNode = ClassHelper.make(identifier);
if (matcher.matches()) { final var classExpression = new ClassExpression(classNode);
final ClassNode classNode = ClassHelper.make(identifier); yield List.of(alias, classExpression);
final ClassExpression classExpression = new ClassExpression(classNode);
yield List.of(alias, classExpression);
} else {
// we need to resolve it
final var isWithPackageMatcher = isWithPackage.matcher(identifier);
if (isWithPackageMatcher.matches()) {
final var resolveResult = this.componentClassNodeResolver.getClassForFqn(identifier);
if (resolveResult.isLeft()) {
final var error = resolveResult.getLeft();
error.setNode(componentNode.getArgs().getType());
state.addError(error);
yield List.of();
} else {
final ClassNode classNode = resolveResult.getRight();
final ClassExpression classExpression = new ClassExpression(classNode); // TODO: pos
yield List.of(alias, classExpression);
}
} else {
final var resolveResult =
this.componentClassNodeResolver.getClassForNameWithoutPackage(identifier);
if (resolveResult.isLeft()) {
final var error = resolveResult.getLeft();
error.setNode(componentNode.getArgs().getType());
state.addError(error);
yield List.of();
} else {
final ClassNode classNode = resolveResult.getRight();
final ClassExpression classExpression = new ClassExpression(classNode); // TODO: pos
yield List.of(alias, classExpression);
}
}
}
} }
case StringComponentTypeNode stringComponentTypeNode -> { case StringComponentTypeNode stringComponentTypeNode -> {
final String identifier = stringComponentTypeNode.getIdentifier(); final String identifier = stringComponentTypeNode.getIdentifier();
@ -127,8 +82,8 @@ public class DefaultComponentTranspiler implements ComponentTranspiler {
} }
// 'h1' | 'MyComponent', MyComponent(.class) // 'h1' | 'MyComponent', MyComponent(.class)
protected ArgumentListExpression getResolveArgs(TypedComponentNode componentNode, TranspilerState state) { protected ArgumentListExpression getResolveArgs(TypedComponentNode componentNode) {
final List<Expression> args = this.getArgsAsList(componentNode, state); final List<Expression> args = this.getArgsAsList(componentNode);
final ArgumentListExpression argsListExpr = new ArgumentListExpression(); final ArgumentListExpression argsListExpr = new ArgumentListExpression();
args.forEach(argsListExpr::addExpression); args.forEach(argsListExpr::addExpression);
return argsListExpr; return argsListExpr;
@ -142,7 +97,7 @@ public class DefaultComponentTranspiler implements ComponentTranspiler {
return new MethodCallExpression( return new MethodCallExpression(
new VariableExpression(state.getRenderContext()), new VariableExpression(state.getRenderContext()),
"resolve", "resolve",
this.getResolveArgs(componentNode, state) this.getResolveArgs(componentNode)
); );
} }

View File

@ -2,7 +2,6 @@ package groowt.view.component.web.transpile;
import groowt.view.component.compiler.ComponentTemplateCompileException; import groowt.view.component.compiler.ComponentTemplateCompileException;
import groowt.view.component.compiler.ComponentTemplateCompileUnit; import groowt.view.component.compiler.ComponentTemplateCompileUnit;
import groowt.view.component.compiler.ComponentTemplateCompilerConfiguration;
import groowt.view.component.web.ast.node.BodyNode; import groowt.view.component.web.ast.node.BodyNode;
import groowt.view.component.web.ast.node.CompilationUnitNode; import groowt.view.component.web.ast.node.CompilationUnitNode;
import groowt.view.component.web.ast.node.PreambleNode; import groowt.view.component.web.ast.node.PreambleNode;
@ -10,7 +9,6 @@ import groowt.view.component.web.compiler.MultipleWebViewComponentCompileErrorsE
import groowt.view.component.web.compiler.WebViewComponentTemplateCompileException; import groowt.view.component.web.compiler.WebViewComponentTemplateCompileException;
import groowt.view.component.web.compiler.WebViewComponentTemplateCompileUnit; import groowt.view.component.web.compiler.WebViewComponentTemplateCompileUnit;
import groowt.view.component.web.transpile.groovy.GroovyUtil; import groowt.view.component.web.transpile.groovy.GroovyUtil;
import groowt.view.component.web.transpile.resolve.ClassLoaderComponentClassNodeResolver;
import org.codehaus.groovy.ast.*; import org.codehaus.groovy.ast.*;
import org.codehaus.groovy.ast.expr.*; import org.codehaus.groovy.ast.expr.*;
import org.codehaus.groovy.ast.stmt.BlockStatement; import org.codehaus.groovy.ast.stmt.BlockStatement;
@ -31,25 +29,13 @@ public class DefaultGroovyTranspiler implements GroovyTranspiler {
private static final Logger logger = LoggerFactory.getLogger(DefaultGroovyTranspiler.class); private static final Logger logger = LoggerFactory.getLogger(DefaultGroovyTranspiler.class);
protected TranspilerConfiguration getConfiguration( protected TranspilerConfiguration getConfiguration() {
ClassLoaderComponentClassNodeResolver classLoaderComponentClassNodeResolver return SimpleTranspilerConfiguration.withDefaults();
) {
return SimpleTranspilerConfiguration.withDefaults(classLoaderComponentClassNodeResolver);
}
protected void addAutomaticImports(WebViewComponentModuleNode moduleNode, TranspilerConfiguration configuration) {
configuration.getImports().forEach(moduleNode::addImport);
configuration.getStaticImports().forEach(staticImport -> moduleNode.addStaticImport(
staticImport.getV1(), staticImport.getV2(), staticImport.getV3()
));
configuration.getStarImports().forEach(moduleNode::addStarImport);
configuration.getStaticStarImports().forEach(moduleNode::addStaticStarImport);
} }
protected WebViewComponentModuleNode initModuleNode( protected WebViewComponentModuleNode initModuleNode(
ComponentTemplateCompileUnit compileUnit, ComponentTemplateCompileUnit compileUnit,
WebViewComponentSourceUnit sourceUnit, WebViewComponentSourceUnit sourceUnit
TranspilerConfiguration configuration
) { ) {
final var moduleNode = new WebViewComponentModuleNode(sourceUnit); final var moduleNode = new WebViewComponentModuleNode(sourceUnit);
sourceUnit.setModuleNode(moduleNode); sourceUnit.setModuleNode(moduleNode);
@ -59,22 +45,16 @@ public class DefaultGroovyTranspiler implements GroovyTranspiler {
moduleNode.setPackageName(defaultPackageName); moduleNode.setPackageName(defaultPackageName);
} }
this.addAutomaticImports(moduleNode, configuration);
return moduleNode; return moduleNode;
} }
protected ClassNode initMainClassNode( protected ClassNode initMainClassNode(ComponentTemplateCompileUnit compileUnit, String templateClassName) {
ComponentTemplateCompileUnit compileUnit,
String templateClassName,
WebViewComponentModuleNode moduleNode
) {
final ClassNode mainClassNode = new ClassNode( final ClassNode mainClassNode = new ClassNode(
compileUnit.getDefaultPackageName() + templateClassName, compileUnit.getDefaultPackageName() + templateClassName,
ACC_PUBLIC, ACC_PUBLIC,
ClassHelper.OBJECT_TYPE ClassHelper.OBJECT_TYPE
); );
mainClassNode.addInterface(TranspilerUtil.COMPONENT_TEMPLATE); mainClassNode.addInterface(TranspilerUtil.COMPONENT_TEMPLATE);
moduleNode.addClass(mainClassNode);
return mainClassNode; return mainClassNode;
} }
@ -216,40 +196,32 @@ public class DefaultGroovyTranspiler implements GroovyTranspiler {
@Override @Override
public WebViewComponentSourceUnit transpile( public WebViewComponentSourceUnit transpile(
ComponentTemplateCompilerConfiguration compilerConfiguration,
WebViewComponentTemplateCompileUnit compileUnit, WebViewComponentTemplateCompileUnit compileUnit,
CompilationUnitNode compilationUnitNode, CompilationUnitNode compilationUnitNode,
String templateClassSimpleName String templateClassSimpleName
) throws ComponentTemplateCompileException { ) throws ComponentTemplateCompileException {
// resolver, transpilerConfiguration, and positionSetter // transpilerConfiguration, and positionSetter
final ClassLoaderComponentClassNodeResolver resolver = new ClassLoaderComponentClassNodeResolver( final var transpilerConfiguration = this.getConfiguration();
compileUnit,
compilerConfiguration.getGroovyClassLoader()
);
final var transpilerConfiguration = this.getConfiguration(resolver);
final PositionSetter positionSetter = transpilerConfiguration.getPositionSetter(); final PositionSetter positionSetter = transpilerConfiguration.getPositionSetter();
// prepare sourceUnit // prepare sourceUnit
final CompilerConfiguration groovyCompilerConfiguration = final CompilerConfiguration groovyCompilerConfiguration = compileUnit.getGroovyCompilationUnit()
compilerConfiguration.getGroovyCompilerConfiguration(); .getConfiguration();
final WebViewComponentSourceUnit sourceUnit = new WebViewComponentSourceUnit( final WebViewComponentSourceUnit sourceUnit = new WebViewComponentSourceUnit(
compileUnit.getDescriptiveName(), compileUnit.getDescriptiveName(),
compileUnit.getGroovyReaderSource(), compileUnit.getGroovyReaderSource(),
groovyCompilerConfiguration, groovyCompilerConfiguration,
compilerConfiguration.getGroovyClassLoader(), compileUnit.getGroovyCompilationUnit().getClassLoader(),
new ErrorCollector(groovyCompilerConfiguration) new ErrorCollector(groovyCompilerConfiguration)
); );
// prepare moduleNode // prepare moduleNode
final WebViewComponentModuleNode moduleNode = this.initModuleNode( final WebViewComponentModuleNode moduleNode = this.initModuleNode(
compileUnit, sourceUnit, transpilerConfiguration compileUnit, sourceUnit
); );
// set resolver's moduleNode
resolver.setModuleNode(moduleNode);
// prepare mainClassNode // prepare mainClassNode
final ClassNode mainClassNode = this.initMainClassNode(compileUnit, templateClassSimpleName, moduleNode); final ClassNode mainClassNode = this.initMainClassNode(compileUnit, templateClassSimpleName);
// handle preamble // handle preamble
final PreambleNode preambleNode = compilationUnitNode.getPreambleNode(); final PreambleNode preambleNode = compilationUnitNode.getPreambleNode();
@ -257,6 +229,9 @@ public class DefaultGroovyTranspiler implements GroovyTranspiler {
this.handlePreamble(templateClassSimpleName, preambleNode, mainClassNode, moduleNode, positionSetter); this.handlePreamble(templateClassSimpleName, preambleNode, mainClassNode, moduleNode, positionSetter);
} }
// Moved here so that moduleNode#getMainClassName reflects fqn of mainClassName
moduleNode.addClass(mainClassNode);
// getRenderer method and render closure // getRenderer method and render closure
// first, getRenderer params // first, getRenderer params
final Parameter componentContextParam = new Parameter(COMPONENT_CONTEXT_TYPE, COMPONENT_CONTEXT_NAME); final Parameter componentContextParam = new Parameter(COMPONENT_CONTEXT_TYPE, COMPONENT_CONTEXT_NAME);

View File

@ -1,14 +1,12 @@
package groowt.view.component.web.transpile; package groowt.view.component.web.transpile;
import groowt.view.component.compiler.ComponentTemplateCompileException; import groowt.view.component.compiler.ComponentTemplateCompileException;
import groowt.view.component.compiler.ComponentTemplateCompilerConfiguration;
import groowt.view.component.web.ast.node.CompilationUnitNode; import groowt.view.component.web.ast.node.CompilationUnitNode;
import groowt.view.component.web.compiler.WebViewComponentTemplateCompileUnit; import groowt.view.component.web.compiler.WebViewComponentTemplateCompileUnit;
public interface GroovyTranspiler { public interface GroovyTranspiler {
WebViewComponentSourceUnit transpile( WebViewComponentSourceUnit transpile(
ComponentTemplateCompilerConfiguration compilerConfiguration,
WebViewComponentTemplateCompileUnit compileUnit, WebViewComponentTemplateCompileUnit compileUnit,
CompilationUnitNode compilationUnitNode, CompilationUnitNode compilationUnitNode,
String templateClassSimpleName String templateClassSimpleName

View File

@ -1,7 +1,6 @@
package groowt.view.component.web.transpile; package groowt.view.component.web.transpile;
import groovy.lang.Tuple3; import groovy.lang.Tuple3;
import groowt.view.component.web.transpile.resolve.ComponentClassNodeResolver;
import org.codehaus.groovy.ast.ClassNode; import org.codehaus.groovy.ast.ClassNode;
import java.util.Map; import java.util.Map;
@ -12,9 +11,8 @@ import static groowt.view.component.web.transpile.TranspilerUtil.*;
public class SimpleTranspilerConfiguration implements TranspilerConfiguration { public class SimpleTranspilerConfiguration implements TranspilerConfiguration {
public static TranspilerConfiguration withDefaults(ComponentClassNodeResolver componentClassNodeResolver) { public static TranspilerConfiguration withDefaults() {
final var c = new SimpleTranspilerConfiguration(); final var c = new SimpleTranspilerConfiguration();
c.setComponentClassNodeResolver(componentClassNodeResolver);
final var ct = new DefaultComponentTranspiler(); final var ct = new DefaultComponentTranspiler();
final PositionSetter ps = new SimplePositionSetter(); final PositionSetter ps = new SimplePositionSetter();
@ -27,7 +25,6 @@ public class SimpleTranspilerConfiguration implements TranspilerConfiguration {
ct.setLeftShiftFactory(lsf); ct.setLeftShiftFactory(lsf);
ct.setBodyTranspiler(bt); ct.setBodyTranspiler(bt);
ct.setValueNodeTranspiler(vnt); ct.setValueNodeTranspiler(vnt);
ct.setComponentClassNodeResolver(componentClassNodeResolver);
c.setComponentTranspiler(ct); c.setComponentTranspiler(ct);
c.setPositionSetter(ps); c.setPositionSetter(ps);
@ -40,7 +37,6 @@ public class SimpleTranspilerConfiguration implements TranspilerConfiguration {
return c; return c;
} }
private ComponentClassNodeResolver componentClassNodeResolver;
private ComponentTranspiler componentTranspiler; private ComponentTranspiler componentTranspiler;
private PositionSetter positionSetter; private PositionSetter positionSetter;
private LeftShiftFactory leftShiftFactory; private LeftShiftFactory leftShiftFactory;
@ -49,14 +45,6 @@ public class SimpleTranspilerConfiguration implements TranspilerConfiguration {
private BodyTranspiler bodyTranspiler; private BodyTranspiler bodyTranspiler;
private ValueNodeTranspiler valueNodeTranspiler; private ValueNodeTranspiler valueNodeTranspiler;
public ComponentClassNodeResolver getComponentClassNodeResolver() {
return Objects.requireNonNull(this.componentClassNodeResolver);
}
public void setComponentClassNodeResolver(ComponentClassNodeResolver componentClassNodeResolver) {
this.componentClassNodeResolver = componentClassNodeResolver;
}
public ComponentTranspiler getComponentTranspiler() { public ComponentTranspiler getComponentTranspiler() {
return Objects.requireNonNull(this.componentTranspiler); return Objects.requireNonNull(this.componentTranspiler);
} }

View File

@ -1,60 +0,0 @@
package groowt.view.component.web.transpile.resolve;
import groowt.util.fp.either.Either;
import groowt.view.component.web.WebViewComponent;
import groowt.view.component.web.compiler.WebViewComponentTemplateCompileUnit;
import org.codehaus.groovy.ast.ClassHelper;
import org.codehaus.groovy.ast.ClassNode;
import java.util.ArrayList;
import java.util.List;
public class CachingComponentClassNodeResolver implements ComponentClassNodeResolver {
private final List<ClassNode> classNodes = new ArrayList<>();
protected final WebViewComponentTemplateCompileUnit compileUnit;
public CachingComponentClassNodeResolver(WebViewComponentTemplateCompileUnit compileUnit) {
this.compileUnit = compileUnit;
}
public void addClass(Class<? extends WebViewComponent> clazz) {
this.classNodes.add(ClassHelper.make(clazz));
}
public void addClassNode(ClassNode classNode) {
this.classNodes.add(classNode);
}
@Override
public Either<ClassNodeResolveException, ClassNode> getClassForFqn(String fqn) {
for (final var classNode : this.classNodes) {
if (classNode.getName().equals(fqn)) {
return Either.right(classNode);
}
}
return Either.left(new ClassNodeResolveException(
this.compileUnit,
fqn,
"Could not resolve ClassNode for fqn: " + fqn,
null
));
}
@Override
public Either<ClassNodeResolveException, ClassNode> getClassForNameWithoutPackage(String nameWithoutPackage) {
for (final var classNode : this.classNodes) {
if (classNode.getNameWithoutPackage().equals(nameWithoutPackage)) {
return Either.right(classNode);
}
}
return Either.left(new ClassNodeResolveException(
this.compileUnit,
nameWithoutPackage,
"Could not resolve ClassNode for nameWithoutPackage: " + nameWithoutPackage,
null
));
}
}

View File

@ -1,53 +0,0 @@
package groowt.view.component.web.transpile.resolve;
import groowt.util.fp.either.Either;
import groowt.view.component.web.compiler.WebViewComponentTemplateCompileUnit;
import org.codehaus.groovy.ast.ClassNode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ClassLoaderComponentClassNodeResolver extends ModuleNodeComponentClassNodeResolver {
private static final Logger logger = LoggerFactory.getLogger(ModuleNodeComponentClassNodeResolver.class);
protected final ClassLoader classLoader;
public ClassLoaderComponentClassNodeResolver(
WebViewComponentTemplateCompileUnit compileUnit,
ClassLoader classLoader
) {
super(compileUnit);
this.classLoader = classLoader;
}
protected final Either<ClassNodeResolveException, ClassNode> resolveWithClassLoader(String fqn) {
logger.debug("Trying to resolve {}", fqn);
try {
Class<?> clazz = this.classLoader.loadClass(ResolveUtil.convertCanonicalNameToBinaryName(fqn));
final var classNode = ResolveUtil.getClassNode(clazz);
return Either.right(classNode);
} catch (ClassNotFoundException classNotFoundException) {
return Either.left(
new ClassNodeResolveException(
this.compileUnit,
fqn,
"Could not find class " + fqn + " with classLoader " +
this.classLoader,
classNotFoundException
)
);
}
}
@Override
public Either<ClassNodeResolveException, ClassNode> getClassForFqn(String fqn) {
return super.getClassForFqn(fqn).flatMapLeft(ignored -> {
final var classLoaderResult = this.resolveWithClassLoader(fqn);
if (classLoaderResult.isRight()) {
this.addClassNode(classLoaderResult.getRight());
}
return classLoaderResult;
});
}
}

View File

@ -1,42 +0,0 @@
package groowt.view.component.web.transpile.resolve;
import groowt.util.fp.either.Either;
import groowt.view.component.web.compiler.WebViewComponentTemplateCompileException;
import groowt.view.component.web.compiler.WebViewComponentTemplateCompileUnit;
import org.codehaus.groovy.ast.ClassNode;
public interface ComponentClassNodeResolver {
final class ClassNodeResolveException extends WebViewComponentTemplateCompileException {
private final String identifier;
public ClassNodeResolveException(
WebViewComponentTemplateCompileUnit compileUnit,
String identifier,
String message
) {
super(compileUnit, message);
this.identifier = identifier;
}
public ClassNodeResolveException(
WebViewComponentTemplateCompileUnit compileUnit,
String identifier,
String message,
Throwable cause
) {
super(compileUnit, message, cause);
this.identifier = identifier;
}
public String getIdentifier() {
return this.identifier;
}
}
Either<ClassNodeResolveException, ClassNode> getClassForFqn(String fqn);
Either<ClassNodeResolveException, ClassNode> getClassForNameWithoutPackage(String nameWithoutPackage);
}

View File

@ -1,84 +0,0 @@
package groowt.view.component.web.transpile.resolve;
import groowt.util.fp.either.Either;
import groowt.view.component.web.compiler.WebViewComponentTemplateCompileUnit;
import org.codehaus.groovy.ast.ClassNode;
import org.codehaus.groovy.ast.ModuleNode;
import java.util.Objects;
public class ModuleNodeComponentClassNodeResolver extends CachingComponentClassNodeResolver {
private ModuleNode moduleNode;
public ModuleNodeComponentClassNodeResolver(WebViewComponentTemplateCompileUnit compileUnit) {
super(compileUnit);
}
public ModuleNode getModuleNode() {
return Objects.requireNonNull(this.moduleNode);
}
public void setModuleNode(ModuleNode moduleNode) {
this.moduleNode = Objects.requireNonNull(moduleNode);
}
@Override
public Either<ClassNodeResolveException, ClassNode> getClassForNameWithoutPackage(String nameWithoutPackage) {
return super.getClassForNameWithoutPackage(nameWithoutPackage).flatMapLeft(ignored -> {
// try regular imports first
final var importedClassNode = this.getModuleNode().getImportType(nameWithoutPackage);
if (importedClassNode != null) {
this.addClassNode(importedClassNode);
return Either.right(importedClassNode);
}
// try star imports
final var starImports = this.getModuleNode().getStarImports();
for (final var starImport : starImports) {
final var packageName = starImport.getPackageName();
final String fqn;
if (!packageName.equals(".") && packageName.endsWith(".")) {
fqn = packageName + nameWithoutPackage;
} else {
fqn = packageName + "." + nameWithoutPackage;
}
final var withPackage = this.getClassForFqn(fqn);
if (withPackage.isRight()) {
return withPackage;
}
}
// try pre-pending package and asking for fqn
final String moduleNodePackageName = this.getModuleNode().getPackageName();
final String packageName;
if (moduleNodePackageName != null) {
packageName = moduleNodePackageName;
} else {
packageName = "";
}
final String fqn;
if (packageName.equals(".") || packageName.isEmpty()) {
fqn = nameWithoutPackage;
} else if (packageName.endsWith(".")) {
fqn = packageName + nameWithoutPackage;
} else {
fqn = packageName + "." + nameWithoutPackage;
}
final var withPackage = this.getClassForFqn(fqn);
if (withPackage.isRight()) {
return withPackage;
} else {
return Either.left(new ClassNodeResolveException(
this.compileUnit,
nameWithoutPackage,
"Cannot resolve " + nameWithoutPackage
+ " from imports, package-local classes, or pre-added classes."
));
}
});
}
}

View File

@ -1,31 +0,0 @@
package groowt.view.component.web.transpile.resolve;
import org.codehaus.groovy.ast.ClassHelper;
import org.codehaus.groovy.ast.ClassNode;
import java.util.regex.Pattern;
public final class ResolveUtil {
private static final Pattern packageSplitter = Pattern.compile("^(?<package>(?>\\p{Ll}[^.]*\\.)*)(?<top>\\p{Lu}[^.]*)(?<members>(?>\\.\\p{Lu}[^.]*)*)$");
public static ClassNode getClassNode(Class<?> clazz) {
return ClassHelper.makeCached(clazz);
}
public static String convertCanonicalNameToBinaryName(String canonicalName) {
final var matcher = packageSplitter.matcher(canonicalName);
if (matcher.matches()) {
return new StringBuilder()
.append(matcher.group("package"))
.append(matcher.group("top"))
.append(matcher.group("members").replaceAll("\\.", "\\$"))
.toString();
} else {
throw new IllegalArgumentException("Cannot split apart " + canonicalName);
}
}
private ResolveUtil() {}
}

View File

@ -1,5 +1,16 @@
package groowt.view.component.web.groovyc package groowt.view.component.web.groovyc
import org.codehaus.groovy.control.CompilerConfiguration import org.codehaus.groovy.control.CompilerConfiguration
import org.codehaus.groovy.control.customizers.ImportCustomizer
(configuration as CompilerConfiguration).tap {
pluginFactory = new WebViewComponentParserPluginFactory()
addCompilationCustomizers(new ImportCustomizer().tap {
addStarImports(
"groowt.view.component.web.lib",
"groowt.view.component.web.runtime",
"groowt.view.component.runtime"
)
})
}
(configuration as CompilerConfiguration).pluginFactory = new WebViewComponentParserPluginFactory()

View File

@ -3,7 +3,6 @@ package groowt.view.component.web.transpiler;
import groowt.view.component.web.compiler.WebViewComponentTemplateCompileUnit; import groowt.view.component.web.compiler.WebViewComponentTemplateCompileUnit;
import groowt.view.component.web.transpile.SimpleTranspilerConfiguration; import groowt.view.component.web.transpile.SimpleTranspilerConfiguration;
import groowt.view.component.web.transpile.TranspilerConfiguration; import groowt.view.component.web.transpile.TranspilerConfiguration;
import groowt.view.component.web.transpile.resolve.CachingComponentClassNodeResolver;
import org.codehaus.groovy.ast.ModuleNode; import org.codehaus.groovy.ast.ModuleNode;
public class DefaultBodyTranspilerTests extends BodyTranspilerTests { public class DefaultBodyTranspilerTests extends BodyTranspilerTests {
@ -13,7 +12,7 @@ public class DefaultBodyTranspilerTests extends BodyTranspilerTests {
WebViewComponentTemplateCompileUnit compileUnit, WebViewComponentTemplateCompileUnit compileUnit,
ModuleNode moduleNode ModuleNode moduleNode
) { ) {
return SimpleTranspilerConfiguration.withDefaults(new CachingComponentClassNodeResolver(compileUnit)); return SimpleTranspilerConfiguration.withDefaults();
} }
} }

View File

@ -1,25 +0,0 @@
package groowt.view.component.web.transpiler;
import org.junit.jupiter.api.Test;
import static groowt.view.component.web.transpile.resolve.ResolveUtil.convertCanonicalNameToBinaryName;
import static org.junit.jupiter.api.Assertions.*;
public class ResolveUtilTests {
@Test
public void abcABC() {
assertEquals("a.b.c.A$B$C", convertCanonicalNameToBinaryName("a.b.c.A.B.C"));
}
@Test
public void ABC() {
assertEquals("A$B$C", convertCanonicalNameToBinaryName("A.B.C"));
}
@Test
public void abcA() {
assertEquals("a.b.c.A", convertCanonicalNameToBinaryName("a.b.c.A"));
}
}

View File

@ -2,7 +2,6 @@ package groowt.view.component.web.transpiler;
import groovy.lang.Tuple2; import groovy.lang.Tuple2;
import groowt.view.component.compiler.ComponentTemplateCompileException; import groowt.view.component.compiler.ComponentTemplateCompileException;
import groowt.view.component.compiler.DefaultComponentTemplateCompilerConfiguration;
import groowt.view.component.compiler.source.StringSource; import groowt.view.component.compiler.source.StringSource;
import groowt.view.component.web.BaseWebViewComponent; import groowt.view.component.web.BaseWebViewComponent;
import groowt.view.component.web.antlr.ParserUtil; import groowt.view.component.web.antlr.ParserUtil;
@ -55,7 +54,6 @@ public abstract class GroovyTranspilerTests {
final var cuNode = astBuilder.buildCompilationUnit(parseResult.getCompilationUnitContext()); final var cuNode = astBuilder.buildCompilationUnit(parseResult.getCompilationUnitContext());
try { try {
this.transpiler.transpile( this.transpiler.transpile(
new DefaultComponentTemplateCompilerConfiguration(),
new DefaultWebViewComponentTemplateCompileUnit( new DefaultWebViewComponentTemplateCompileUnit(
"<anonymous string source>", "<anonymous string source>",
AnonymousWebViewComponent.class, AnonymousWebViewComponent.class,

View File

@ -1,8 +1,10 @@
#/usr/bin/env bash #/usr/bin/env bash
VERSION="0.1.2"
if [ "\$1" == "--debug" ]; then if [ "\$1" == "--debug" ]; then
shift shift
gradle -q toolsJar && java -agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=*:8192 -cp build/libs/web-view-components-compiler-tools-0.1.1.jar $mainClassName "\$@" gradle -q toolsJar && java -agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=*:8192 -cp build/libs/web-view-components-compiler-tools-\$VERSION.jar $mainClassName "\$@"
else else
gradle -q toolsJar && java -cp build/libs/web-view-components-compiler-tools-0.1.1.jar $mainClassName "\$@" gradle -q toolsJar && java -cp build/libs/web-view-components-compiler-tools-\$VERSION.jar $mainClassName "\$@"
fi fi

View File

@ -9,6 +9,7 @@ import org.apache.logging.log4j.core.LoggerContext;
import org.codehaus.groovy.control.CompilationFailedException; import org.codehaus.groovy.control.CompilationFailedException;
import org.codehaus.groovy.control.CompilationUnit; import org.codehaus.groovy.control.CompilationUnit;
import org.codehaus.groovy.control.CompilerConfiguration; import org.codehaus.groovy.control.CompilerConfiguration;
import org.codehaus.groovy.control.customizers.ImportCustomizer;
import picocli.CommandLine; import picocli.CommandLine;
import picocli.CommandLine.Command; import picocli.CommandLine.Command;
@ -71,6 +72,15 @@ public final class GroovyWvcCompiler implements Callable<Integer> {
public Integer doCompile() { public Integer doCompile() {
final CompilerConfiguration configuration = new CompilerConfiguration(); final CompilerConfiguration configuration = new CompilerConfiguration();
final var addGroowtImports = new ImportCustomizer();
addGroowtImports.addStarImports(
"groowt.view.component.web.lib",
"groowt.view.component.web.runtime",
"groowt.view.component.runtime"
);
configuration.addCompilationCustomizers(addGroowtImports);
configuration.setPluginFactory(new WebViewComponentParserPluginFactory()); configuration.setPluginFactory(new WebViewComponentParserPluginFactory());
final CompilationUnit compilationUnit = new CompilationUnit(configuration); final CompilationUnit compilationUnit = new CompilationUnit(configuration);

View File

@ -133,6 +133,10 @@ java {
withSourcesJar() withSourcesJar()
} }
jar {
archiveBaseName = 'groowt-web-view-components'
}
publishing { publishing {
publications { publications {
create('webViewComponents', MavenPublication) { create('webViewComponents', MavenPublication) {

View File

@ -5,7 +5,6 @@ import groowt.view.component.ComponentRenderException
import groowt.view.component.context.ComponentContext import groowt.view.component.context.ComponentContext
import groowt.view.component.context.ComponentScope.TypeAndFactory import groowt.view.component.context.ComponentScope.TypeAndFactory
import groowt.view.component.factory.ComponentFactory import groowt.view.component.factory.ComponentFactory
import groowt.view.component.web.WithHtml
class IntrinsicHtml extends DelegatingWebViewComponent implements WithHtml { class IntrinsicHtml extends DelegatingWebViewComponent implements WithHtml {

View File

@ -1,6 +1,7 @@
package groowt.view.component.web.lib package groowt.view.component.web.lib
import groowt.view.View import groowt.view.View
import groowt.view.component.ComponentRenderException
import groowt.view.component.runtime.DefaultComponentWriter import groowt.view.component.runtime.DefaultComponentWriter
class Outlet extends DelegatingWebViewComponent { class Outlet extends DelegatingWebViewComponent {
@ -14,6 +15,11 @@ class Outlet extends DelegatingWebViewComponent {
@Override @Override
protected View getDelegate() { protected View getDelegate() {
return { Writer w -> return { Writer w ->
if (!context.hasAncestor(OutletContainer)) {
throw new ComponentRenderException(
"<Outlet> is being used outside of a component implementing OutletContainer."
)
}
def cw = new DefaultComponentWriter(w, context.renderContext, context) def cw = new DefaultComponentWriter(w, context.renderContext, context)
givenChildren.each { cw << it } givenChildren.each { cw << it }
} }

View File

@ -0,0 +1,5 @@
package groowt.view.component.web.lib
import groowt.view.component.web.WebViewComponent
interface OutletContainer extends WebViewComponent {}

View File

@ -1,4 +1,4 @@
package groowt.view.component.web package groowt.view.component.web.lib
trait WithHtml { trait WithHtml {

View File

@ -4,13 +4,14 @@ import groowt.view.component.ViewComponent;
import groowt.view.component.compiler.AbstractComponentTemplateCompileUnit; import groowt.view.component.compiler.AbstractComponentTemplateCompileUnit;
import groowt.view.component.compiler.ComponentTemplateCompileException; import groowt.view.component.compiler.ComponentTemplateCompileException;
import groowt.view.component.compiler.ComponentTemplateCompileResult; import groowt.view.component.compiler.ComponentTemplateCompileResult;
import groowt.view.component.compiler.ComponentTemplateCompilerConfiguration;
import groowt.view.component.compiler.source.ComponentTemplateSource; import groowt.view.component.compiler.source.ComponentTemplateSource;
import groowt.view.component.compiler.source.FileSource; import groowt.view.component.compiler.source.FileSource;
import groowt.view.component.compiler.source.URISource; import groowt.view.component.compiler.source.URISource;
import groowt.view.component.compiler.source.URLSource; import groowt.view.component.compiler.source.URLSource;
import org.codehaus.groovy.control.CompilationUnit; import org.codehaus.groovy.control.CompilationUnit;
import org.codehaus.groovy.control.CompilerConfiguration;
import org.codehaus.groovy.control.Janitor; import org.codehaus.groovy.control.Janitor;
import org.codehaus.groovy.control.customizers.ImportCustomizer;
import org.codehaus.groovy.control.io.ReaderSource; import org.codehaus.groovy.control.io.ReaderSource;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
@ -20,8 +21,20 @@ import java.net.URI;
public class DefaultWebViewComponentTemplateCompileUnit extends AbstractComponentTemplateCompileUnit public class DefaultWebViewComponentTemplateCompileUnit extends AbstractComponentTemplateCompileUnit
implements ReaderSource, WebViewComponentTemplateCompileUnit { implements ReaderSource, WebViewComponentTemplateCompileUnit {
private static CompilationUnit getCompilationUnit() {
final var configuration = new CompilerConfiguration();
final var addGroowtImports = new ImportCustomizer();
addGroowtImports.addStarImports(
"groowt.view.component.web.lib",
"groowt.view.component.web.runtime",
"groowt.view.component.runtime"
);
configuration.addCompilationCustomizers(addGroowtImports);
return new CompilationUnit(configuration);
}
private final String defaultPackageName; private final String defaultPackageName;
private final CompilationUnit groovyCompilationUnit = new CompilationUnit(); private final CompilationUnit groovyCompilationUnit;
public DefaultWebViewComponentTemplateCompileUnit( public DefaultWebViewComponentTemplateCompileUnit(
String descriptiveName, String descriptiveName,
@ -35,6 +48,7 @@ public class DefaultWebViewComponentTemplateCompileUnit extends AbstractComponen
} else { } else {
this.defaultPackageName = defaultPackageName; this.defaultPackageName = defaultPackageName;
} }
this.groovyCompilationUnit = getCompilationUnit();
} }
@Override @Override
@ -53,9 +67,8 @@ public class DefaultWebViewComponentTemplateCompileUnit extends AbstractComponen
} }
@Override @Override
public ComponentTemplateCompileResult compile(ComponentTemplateCompilerConfiguration configuration) public ComponentTemplateCompileResult compile() throws ComponentTemplateCompileException {
throws ComponentTemplateCompileException { final WebViewComponentTemplateCompiler compiler = WebViewComponentTemplateCompiler.get();
final WebViewComponentTemplateCompiler compiler = WebViewComponentTemplateCompiler.get(configuration);
return compiler.compile(this); return compiler.compile(this);
} }

View File

@ -9,10 +9,10 @@ public interface WebViewComponentTemplateCompiler
extends ComponentTemplateCompiler<WebViewComponentTemplateCompileUnit> { extends ComponentTemplateCompiler<WebViewComponentTemplateCompileUnit> {
static WebViewComponentTemplateCompiler get() { static WebViewComponentTemplateCompiler get() {
return get(new DefaultComponentTemplateCompilerConfiguration()); return get(new WebViewComponentTemplateCompilerConfiguration());
} }
static WebViewComponentTemplateCompiler get(ComponentTemplateCompilerConfiguration configuration) { static WebViewComponentTemplateCompiler get(WebViewComponentTemplateCompilerConfiguration configuration) {
final ServiceLoader<WebViewComponentTemplateCompilerFactory> factoryServiceLoader = final ServiceLoader<WebViewComponentTemplateCompilerFactory> factoryServiceLoader =
ServiceLoader.load(WebViewComponentTemplateCompilerFactory.class); ServiceLoader.load(WebViewComponentTemplateCompilerFactory.class);
final var factory = factoryServiceLoader.findFirst() final var factory = factoryServiceLoader.findFirst()

View File

@ -0,0 +1,17 @@
package groowt.view.component.web.compiler;
import org.codehaus.groovy.control.CompilePhase;
public class WebViewComponentTemplateCompilerConfiguration {
private CompilePhase compilePhase = CompilePhase.CLASS_GENERATION;
public CompilePhase getCompilePhase() {
return this.compilePhase;
}
public void setCompilePhase(CompilePhase compilePhase) {
this.compilePhase = compilePhase;
}
}

View File

@ -1,7 +1,5 @@
package groowt.view.component.web.compiler; package groowt.view.component.web.compiler;
import groowt.view.component.compiler.ComponentTemplateCompilerConfiguration;
public interface WebViewComponentTemplateCompilerFactory { public interface WebViewComponentTemplateCompilerFactory {
WebViewComponentTemplateCompiler create(ComponentTemplateCompilerConfiguration configuration); WebViewComponentTemplateCompiler create(WebViewComponentTemplateCompilerConfiguration configuration);
} }

View File

@ -1,17 +1,35 @@
package groowt.view.component.web.lib package groowt.view.component.web.lib
import groowt.view.component.web.WebViewComponentContext
import groowt.view.component.web.WebViewComponentScope
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
class OutletTests extends AbstractWebViewComponentTests { class OutletTests extends AbstractWebViewComponentTests {
static final class DummyOutletContainer extends Echo implements OutletContainer {
DummyOutletContainer() {
super([:])
}
}
@Override
void configureContext(WebViewComponentContext context) {
context.configureRootScope(WebViewComponentScope) {
addWithNoArgConstructor(DummyOutletContainer)
}
}
@Test @Test
void smokeScreen() { void smokeScreen() {
doTest('<Outlet />', '') doTest('<OutletTests.DummyOutletContainer><Outlet /></OutletTests.DummyOutletContainer>', '')
} }
@Test @Test
void withChildren() { void withChildren() {
doTest('<Echo items={[0, 1, 2]}><Outlet children={items} /></Echo>', '012') doTest('<OutletTests.DummyOutletContainer><Echo items={[0, 1, 2]}><Outlet children={items} /></Echo></OutletTests.DummyOutletContainer>', '012')
} }
} }