Imports in gsp/gst files now working.

This commit is contained in:
JesseBrault0709 2023-06-12 08:35:56 +02:00
parent bc28a00cfc
commit 956642339c
18 changed files with 218 additions and 45 deletions

View File

@ -23,6 +23,8 @@ import java.util.function.Consumer
final class DefaultBuildScriptConfiguratorFactory implements BuildScriptConfiguratorFactory { final class DefaultBuildScriptConfiguratorFactory implements BuildScriptConfiguratorFactory {
private final File baseDir private final File baseDir
private final ClassLoader classLoader
private final Collection<URL> scriptBaseUrls
@Override @Override
Consumer<BuildScriptBase> get() { Consumer<BuildScriptBase> get() {
@ -34,9 +36,9 @@ final class DefaultBuildScriptConfiguratorFactory implements BuildScriptConfigur
types { types {
textTypes << TextTypes.MARKDOWN textTypes << TextTypes.MARKDOWN
pageTypes << PageTypes.GSP pageTypes << PageTypes.getGsp(['.gsp', '.ssg.gst'], this.classLoader, this.scriptBaseUrls)
templateTypes << TemplateTypes.GSP templateTypes << TemplateTypes.getGsp(['.gsp', '.ssg.gst'], this.classLoader, this.scriptBaseUrls)
partTypes << PartTypes.GSP partTypes << PartTypes.getGsp(['.gsp', '.ssg.gst'], this.classLoader, this.scriptBaseUrls)
} }
sources { base, types -> sources { base, types ->

View File

@ -11,7 +11,11 @@ import groovy.transform.NullCheck
@EqualsAndHashCode @EqualsAndHashCode
final class GspPageRenderer implements PageRenderer { final class GspPageRenderer implements PageRenderer {
private final StandardGspRenderer gspRenderer = new StandardGspRenderer(this.class.classLoader) private final StandardGspRenderer gspRenderer
GspPageRenderer(ClassLoader classLoader, Collection<URL> urls) {
this.gspRenderer = new StandardGspRenderer(classLoader, urls)
}
@Override @Override
Result<String> render( Result<String> render(

View File

@ -2,7 +2,12 @@ package com.jessebrault.ssg.page
final class PageTypes { final class PageTypes {
static final PageType GSP = new PageType(['.gsp'], new GspPageRenderer()) @Deprecated
static final PageType GSP = new PageType(['.gsp'], new GspPageRenderer(PageTypes.classLoader, []))
static PageType getGsp(Collection<String> extensions, ClassLoader classLoader, Collection<URL> urls) {
new PageType(extensions, new GspPageRenderer(classLoader, urls))
}
private PageTypes() {} private PageTypes() {}

View File

@ -13,7 +13,11 @@ import static java.util.Objects.requireNonNull
@EqualsAndHashCode @EqualsAndHashCode
final class GspPartRenderer implements PartRenderer { final class GspPartRenderer implements PartRenderer {
private final StandardGspRenderer gspRenderer = new StandardGspRenderer(this.class.classLoader) private final StandardGspRenderer gspRenderer
GspPartRenderer(ClassLoader classLoader, Collection<URL> urls) {
this.gspRenderer = new StandardGspRenderer(classLoader, urls)
}
@Override @Override
Result<String> render( Result<String> render(

View File

@ -2,7 +2,12 @@ package com.jessebrault.ssg.part
final class PartTypes { final class PartTypes {
static final PartType GSP = new PartType(['.gsp'], new GspPartRenderer()) @Deprecated
static final PartType GSP = new PartType(['.gsp'], new GspPartRenderer(PartTypes.classLoader, []))
static PartType getGsp(Collection<String> extensions, ClassLoader classLoader, Collection<URL> scriptBaseUrls) {
new PartType(extensions, new GspPartRenderer(classLoader, scriptBaseUrls))
}
private PartTypes() {} private PartTypes() {}

View File

@ -13,8 +13,8 @@ final class StandardGspRenderer {
private final TemplateCreator templateCreator private final TemplateCreator templateCreator
StandardGspRenderer(ClassLoader parentClassLoader) { StandardGspRenderer(ClassLoader parentClassLoader, Collection<URL> urls) {
this.templateCreator = new GroovyTemplateCreator(ExtendedGstParser::new, [], parentClassLoader, true) this.templateCreator = new GroovyTemplateCreator(ExtendedGstParser::new, urls, parentClassLoader, true)
} }
Result<String> render( Result<String> render(

View File

@ -12,7 +12,11 @@ import groovy.transform.NullCheck
@EqualsAndHashCode @EqualsAndHashCode
final class GspTemplateRenderer implements TemplateRenderer { final class GspTemplateRenderer implements TemplateRenderer {
private final StandardGspRenderer gspRenderer = new StandardGspRenderer(this.class.classLoader) private final StandardGspRenderer gspRenderer
GspTemplateRenderer(ClassLoader parentClassLoader, Collection<URL> urls) {
this.gspRenderer = new StandardGspRenderer(parentClassLoader, urls)
}
@Override @Override
Result<String> render( Result<String> render(

View File

@ -2,7 +2,16 @@ package com.jessebrault.ssg.template
final class TemplateTypes { final class TemplateTypes {
static final TemplateType GSP = new TemplateType(['.gsp'], new GspTemplateRenderer()) @Deprecated
static final TemplateType GSP = new TemplateType(['.gsp'], new GspTemplateRenderer(TemplateTypes.classLoader, []))
static TemplateType getGsp(
Collection<String> extensions,
ClassLoader classLoader,
Collection<URL> scriptBaseUrls
) {
new TemplateType(extensions, new GspTemplateRenderer(classLoader, scriptBaseUrls))
}
private TemplateTypes() {} private TemplateTypes() {}

View File

@ -6,12 +6,15 @@ import com.jessebrault.ssg.util.Result
final class GspPageRendererTests implements StandardDslConsumerTests { final class GspPageRendererTests implements StandardDslConsumerTests {
private final PageRenderer renderer = new GspPageRenderer() private static GspPageRenderer getRenderer(ClassLoader classLoader, Collection<URL> urls) {
new GspPageRenderer(classLoader, urls)
}
@Override @Override
Result<String> render(String scriptlet, RenderContext context) { Result<String> render(String scriptlet, RenderContext context, ClassLoader classLoader, Collection<URL> urls) {
this.renderer.render( def renderer = getRenderer(classLoader, urls)
new Page('', new PageType([], this.renderer), scriptlet), renderer.render(
new Page('', new PageType([], renderer), scriptlet),
context context
) )
} }

View File

@ -4,6 +4,7 @@ import com.jessebrault.ssg.dsl.StandardDslConsumerTests
import com.jessebrault.ssg.render.RenderContext import com.jessebrault.ssg.render.RenderContext
import com.jessebrault.ssg.text.Text import com.jessebrault.ssg.text.Text
import com.jessebrault.ssg.util.Result import com.jessebrault.ssg.util.Result
import org.jetbrains.annotations.Nullable
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
import static com.jessebrault.ssg.text.TextMocks.renderableText import static com.jessebrault.ssg.text.TextMocks.renderableText
@ -13,16 +14,24 @@ import static org.junit.jupiter.api.Assertions.assertTrue
final class GspPartRendererTests implements StandardDslConsumerTests { final class GspPartRendererTests implements StandardDslConsumerTests {
private final PartRenderer renderer = new GspPartRenderer() private static GspPartRenderer getRenderer(
ClassLoader classLoader = GspPartRendererTests.classLoader,
Collection<URL> urls = []
) {
new GspPartRenderer(classLoader, urls)
}
private Result<String> doRender( private static Result<String> doRender(
String scriptlet, String scriptlet,
RenderContext context, RenderContext context,
Map binding = [:], Map binding = [:],
Text text = null @Nullable Text text = null,
ClassLoader classLoader = GspPartRendererTests.classLoader,
Collection<URL> urls = []
) { ) {
this.renderer.render( def renderer = getRenderer(classLoader, urls)
new Part('', new PartType([], this.renderer), scriptlet), renderer.render(
new Part('', new PartType([], renderer), scriptlet),
binding, binding,
context, context,
text text
@ -30,22 +39,23 @@ final class GspPartRendererTests implements StandardDslConsumerTests {
} }
@Override @Override
Result<String> render(String scriptlet, RenderContext context) { Result<String> render(String scriptlet, RenderContext context, ClassLoader classLoader, Collection<URL> urls) {
this.doRender(scriptlet, context) doRender(scriptlet, context, [:], null, classLoader, urls)
} }
@Test @Test
void rendersWithBinding() { void rendersWithBinding() {
this.checkResult( this.checkResult(
'Hello, World!', 'Hello, World!',
this.doRender('<%= binding.greeting %>', new RenderContext(), [greeting: 'Hello, World!']) doRender('<%= binding.greeting %>', new RenderContext(), [greeting: 'Hello, World!'])
) )
} }
@Test @Test
void textAvailable() { void textAvailable() {
this.checkResult('Hello, World!', this.renderer.render( def renderer = getRenderer()
new Part('', new PartType([], this.renderer), '<%= text.render() %>'), this.checkResult('Hello, World!', renderer.render(
new Part('', new PartType([], renderer), '<%= text.render() %>'),
[:], [:],
new RenderContext(), new RenderContext(),
renderableText('Hello, World!') renderableText('Hello, World!')
@ -54,7 +64,8 @@ final class GspPartRendererTests implements StandardDslConsumerTests {
@Test @Test
void nestedPartDiagnosticBubblesUp() { void nestedPartDiagnosticBubblesUp() {
def partType = new PartType([], this.renderer) def renderer = getRenderer()
def partType = new PartType([], renderer)
def nestedProblemPart = new Part( def nestedProblemPart = new Part(
'nestedProblem.gsp', 'nestedProblem.gsp',
partType, partType,
@ -65,7 +76,7 @@ final class GspPartRendererTests implements StandardDslConsumerTests {
partType, partType,
'<% parts["nestedProblem.gsp"].render() %>' '<% parts["nestedProblem.gsp"].render() %>'
) )
def r = this.renderer.render( def r = renderer.render(
callerPart, callerPart,
[:], [:],
new RenderContext(parts: [nestedProblemPart]), new RenderContext(parts: [nestedProblemPart]),
@ -79,7 +90,8 @@ final class GspPartRendererTests implements StandardDslConsumerTests {
@Test @Test
void nestedPartIsBlankWhenThrowingExceptionButCallerRendered() { void nestedPartIsBlankWhenThrowingExceptionButCallerRendered() {
def partType = new PartType([], this.renderer) def renderer = getRenderer()
def partType = new PartType([], renderer)
def nestedProblemPart = new Part( def nestedProblemPart = new Part(
'nestedProblem.gsp', 'nestedProblem.gsp',
partType, partType,
@ -90,7 +102,7 @@ final class GspPartRendererTests implements StandardDslConsumerTests {
partType, partType,
'Hello, World!<% parts["nestedProblem.gsp"].render() %>' 'Hello, World!<% parts["nestedProblem.gsp"].render() %>'
) )
def r = this.renderer.render( def r = renderer.render(
callerPart, callerPart,
[:], [:],
new RenderContext(parts: [nestedProblemPart]), new RenderContext(parts: [nestedProblemPart]),

View File

@ -13,21 +13,34 @@ import static org.junit.jupiter.api.Assertions.assertEquals
final class GspTemplateRendererTests implements StandardDslConsumerTests { final class GspTemplateRendererTests implements StandardDslConsumerTests {
private final TemplateRenderer renderer = new GspTemplateRenderer() private static TemplateRenderer getRenderer(
ClassLoader classLoader = GspTemplateRendererTests.classLoader,
Collection<URL> urls = []
) {
new GspTemplateRenderer(classLoader, urls)
}
private Result<String> doRender(String scriptlet, Text text, RenderContext context) { private static Result<String> doRender(
this.renderer.render(new Template('', new TemplateType([], this.renderer), scriptlet), text, context) String scriptlet,
Text text,
RenderContext context,
ClassLoader classLoader,
Collection<URL> urls
) {
def renderer = getRenderer(classLoader, urls)
renderer.render(new Template('', new TemplateType([], renderer), scriptlet), text, context)
} }
@Override @Override
Result<String> render(String scriptlet, RenderContext context) { Result<String> render(String scriptlet, RenderContext context, ClassLoader classLoader, Collection<URL> urls) {
this.doRender(scriptlet, blankText(), context) doRender(scriptlet, blankText(), context, classLoader, urls)
} }
@Test @Test
void textAvailableToRender() { void textAvailableToRender() {
def template = new Template('', new TemplateType([], this.renderer), '<%= text.render() %>') def renderer = getRenderer()
def r = this.renderer.render( def template = new Template('', new TemplateType([], renderer), '<%= text.render() %>')
def r = renderer.render(
template, template,
renderableText('Hello, World!'), renderableText('Hello, World!'),
new RenderContext() new RenderContext()

View File

@ -33,21 +33,32 @@ import static org.mockito.Mockito.*
@ExtendWith(MockitoExtension) @ExtendWith(MockitoExtension)
interface StandardDslConsumerTests { interface StandardDslConsumerTests {
Result<String> render(String scriptlet, RenderContext context) Result<String> render(String scriptlet, RenderContext context, ClassLoader classLoader, Collection<URL> urls)
default void checkResult(String expected, Result<String> result) { default void checkResult(String expected, Result<String> result) {
assertEmptyDiagnostics(result) assertEmptyDiagnostics(result)
assertEquals(expected, result.get()) assertEquals(expected, result.get())
} }
default void doDslRenderTest(String expected, String scriptlet, RenderContext context = null) { default void doDslRenderTest(
this.checkResult(expected, this.render(scriptlet, context ?: new RenderContext())) String expected,
String scriptlet,
RenderContext context = new RenderContext(),
ClassLoader classLoader = this.class.classLoader,
Collection<URL> urls = []
) {
this.checkResult(expected, this.render(scriptlet, context, classLoader, urls))
} }
default void doDslAssertionTest(String scriptlet, RenderContext context = null) { default void doDslAssertionTest(
String scriptlet,
RenderContext context = new RenderContext(),
ClassLoader classLoader = this.class.classLoader,
Collection<URL> urls = []
) {
Result<String> result = null Result<String> result = null
try { try {
result = this.render(scriptlet, context ?: new RenderContext()) result = this.render(scriptlet, context, classLoader, urls)
} catch (Throwable e) { } catch (Throwable e) {
fail(e) fail(e)
} }
@ -242,4 +253,39 @@ interface StandardDslConsumerTests {
) )
} }
@Test
default void importFromClasspath() {
this.doDslAssertionTest('<%@ import com.jessebrault.ssg.dsl.Greeter %><% assert Greeter %>')
}
@Test
default void importFromClasspathIsClass() {
this.doDslAssertionTest(
'<%@ import com.jessebrault.ssg.dsl.Greeter %><% assert Greeter instanceof Class %>'
)
}
@Test
default void withConfiguredUrls() {
def parentClassLoader = this.class.classLoader
def greeterText = StandardDslConsumerTestsUtil.readResource(
'com/jessebrault/ssg/dsl/TmpGreeter.groovy', parentClassLoader
)
if (greeterText == null) {
fail('Could not load greeterText')
}
def greeterBaseUrl = StandardDslConsumerTestsUtil.writeGroovyClass(
'TmpGreeter',
['com', 'jessebrault', 'ssg', 'tmp'],
greeterText
)
this.doDslRenderTest(
"Hello, World!",
"<%@ import com.jessebrault.ssg.tmp.TmpGreeter %><%= new TmpGreeter().greet() %>",
new RenderContext(),
parentClassLoader,
[greeterBaseUrl]
)
}
} }

View File

@ -0,0 +1,33 @@
package com.jessebrault.ssg.dsl
import org.jetbrains.annotations.Nullable
final class StandardDslConsumerTestsUtil {
static @Nullable String readResource(String name, ClassLoader classLoader) {
def resource = classLoader.getResourceAsStream(name)
if (resource != null) {
def writer = new StringWriter()
resource.withReader {
it.transferTo(writer)
}
writer.toString()
} else {
null
}
}
static URL writeGroovyClass(String name, List<String> packageParts, String text) {
def tmpDir = File.createTempDir("standardDslConsumerTestsUtil")
def packageFile = new File(tmpDir, (packageParts.inject('') { acc, part ->
acc + File.separator + part
}) as String)
def classFile = new File(packageFile, "${ name }.groovy")
classFile.createParentDirectories()
classFile.write(text)
tmpDir.toURI().toURL()
}
private StandardDslConsumerTestsUtil() {}
}

View File

@ -0,0 +1,11 @@
package com.jessebrault.ssg.dsl
@SuppressWarnings('unused')
class Greeter {
@SuppressWarnings('GrMethodMayBeStatic')
String greet() {
'Hello, World!'
}
}

View File

@ -0,0 +1,12 @@
//file:noinspection GrPackage
package com.jessebrault.ssg.tmp
@SuppressWarnings('unused')
class TmpGreeter {
@SuppressWarnings('GrMethodMayBeStatic')
String greet() {
'Hello, World!'
}
}

View File

@ -47,7 +47,9 @@ abstract class AbstractBuildCommand extends AbstractSubCommand {
new File('.'), new File('.'),
this.buildScript, this.buildScript,
this.buildSrcDirs, this.buildSrcDirs,
this.scriptArgs this.scriptArgs,
this.class.classLoader,
this.buildSrcDirs.collect { it.toURI().toURL() }
) )
} }

View File

@ -13,6 +13,8 @@ final class CliBasedStaticSiteGenerator implements StaticSiteGenerator {
private final File buildScript private final File buildScript
private final Collection<File> buildSrcDirs private final Collection<File> buildSrcDirs
private final Map<String, String> scriptArgs private final Map<String, String> scriptArgs
private final ClassLoader classLoader
private final Collection<URL> scriptBaseUrls
private StaticSiteGenerator staticSiteGenerator private StaticSiteGenerator staticSiteGenerator
@ -20,19 +22,23 @@ final class CliBasedStaticSiteGenerator implements StaticSiteGenerator {
File baseDir, File baseDir,
File buildScript, File buildScript,
Collection<File> buildSrcDirs, Collection<File> buildSrcDirs,
Map<String, String> scriptArgs Map<String, String> scriptArgs,
ClassLoader classLoader,
Collection<URL> scriptBaseUrls
) { ) {
this.baseDir = baseDir this.baseDir = baseDir
this.buildScript = buildScript this.buildScript = buildScript
this.buildSrcDirs = buildSrcDirs this.buildSrcDirs = buildSrcDirs
this.scriptArgs = scriptArgs this.scriptArgs = scriptArgs
this.classLoader = classLoader
this.scriptBaseUrls = scriptBaseUrls
} }
@Override @Override
boolean doBuild(String buildName, Consumer<Collection<Diagnostic>> diagnosticsConsumer) { boolean doBuild(String buildName, Consumer<Collection<Diagnostic>> diagnosticsConsumer) {
if (this.staticSiteGenerator == null) { if (this.staticSiteGenerator == null) {
this.staticSiteGenerator = new BuildScriptBasedStaticSiteGenerator( this.staticSiteGenerator = new BuildScriptBasedStaticSiteGenerator(
[new DefaultBuildScriptConfiguratorFactory(this.baseDir)], [new DefaultBuildScriptConfiguratorFactory(this.baseDir, this.classLoader, this.scriptBaseUrls)],
this.buildScript == new File('ssgBuilds.groovy') || this.buildScript.exists() this.buildScript == new File('ssgBuilds.groovy') || this.buildScript.exists()
? new File(this.baseDir, this.buildScript.path) ? new File(this.baseDir, this.buildScript.path)
: null, : null,

View File

@ -18,7 +18,9 @@ final class CliBasedStaticSiteGeneratorTests {
tempDir, tempDir,
new File('ssgBuilds.groovy'), new File('ssgBuilds.groovy'),
[new File('buildSrc')], [new File('buildSrc')],
[:] [:],
this.class.classLoader,
[new File(tempDir, 'buildSrc').toURI().toURL()]
) )
def diagnostics = [] as Collection<Diagnostic> def diagnostics = [] as Collection<Diagnostic>
assertTrue(ssg.doBuild('production', diagnostics.&addAll)) assertTrue(ssg.doBuild('production', diagnostics.&addAll))