Massive amount of work: 1. html specs, 2. classloaders.

This commit is contained in:
JesseBrault0709 2023-06-14 11:20:31 +02:00
parent b741765b24
commit f44f2df797
33 changed files with 574 additions and 462 deletions

View File

@ -8,7 +8,7 @@ repositories {
dependencies { dependencies {
// https://archiva.jessebrault.com/#artifact/com.jessebrault.gst/lib // https://archiva.jessebrault.com/#artifact/com.jessebrault.gst/lib
implementation 'com.jessebrault.gst:lib:0.0.3' implementation 'com.jessebrault.gst:lib:0.0.5'
// https://mvnrepository.com/artifact/org.apache.groovy/groovy-templates // https://mvnrepository.com/artifact/org.apache.groovy/groovy-templates
implementation 'org.apache.groovy:groovy-templates:4.0.12' implementation 'org.apache.groovy:groovy-templates:4.0.12'

View File

@ -2,7 +2,7 @@ package com.jessebrault.ssg
import com.jessebrault.ssg.buildscript.Build import com.jessebrault.ssg.buildscript.Build
import com.jessebrault.ssg.buildscript.BuildScriptConfiguratorFactory import com.jessebrault.ssg.buildscript.BuildScriptConfiguratorFactory
import com.jessebrault.ssg.buildscript.BuildScripts import com.jessebrault.ssg.buildscript.BuildScriptRunner
import com.jessebrault.ssg.util.Diagnostic import com.jessebrault.ssg.util.Diagnostic
import org.jetbrains.annotations.Nullable import org.jetbrains.annotations.Nullable
import org.slf4j.Logger import org.slf4j.Logger
@ -18,45 +18,50 @@ final class BuildScriptBasedStaticSiteGenerator implements StaticSiteGenerator {
private static final Marker enter = MarkerFactory.getMarker('enter') private static final Marker enter = MarkerFactory.getMarker('enter')
private static final Marker exit = MarkerFactory.getMarker('exit') private static final Marker exit = MarkerFactory.getMarker('exit')
private final GroovyScriptEngine engine
private final Collection<BuildScriptConfiguratorFactory> configuratorFactories
private final @Nullable File buildScript private final @Nullable File buildScript
private final Map<String, Object> scriptArgs
private final Collection<Build> builds = [] private final Collection<Build> builds = []
private boolean ranBuildScript = false private boolean ranBuildScript = false
private GroovyClassLoader buildScriptClassLoader
/**
* @param buildScriptClassLoaderUrls the urls to pass to the buildScriptRunner's GroovyClassLoader.
* These should include the URL needed to find the given buildScript file, if present, as
* well as any script dependencies (such as the buildSrc dir).
* @param buildScript The buildScript File, may be <code>null</code>.
*/
BuildScriptBasedStaticSiteGenerator( BuildScriptBasedStaticSiteGenerator(
GroovyScriptEngine engine, Collection<URL> buildScriptClassLoaderUrls,
Collection<BuildScriptConfiguratorFactory> configuratorFactories = [], @Nullable File buildScript = null
@Nullable File buildScript = null,
Map<String, Object> scriptArgs = [:]
) { ) {
this.engine = engine
this.configuratorFactories = configuratorFactories
this.buildScript = buildScript this.buildScript = buildScript
this.scriptArgs = scriptArgs
} }
private void runBuildScript() { private void runBuildScript(
logger.trace(enter, '') Collection<BuildScriptConfiguratorFactory> configuratorFactories,
Map<String, Object> buildScriptArgs
) {
logger.trace(enter, 'configuratorFactories: {}, buildScriptArgs: {}', configuratorFactories, buildScriptArgs)
if (this.buildScript == null) { if (this.buildScript == null) {
logger.info('no specified build script; using defaults') logger.info('no specified build script; using defaults')
def result = BuildScripts.runBuildScript { base -> def result = BuildScriptRunner.runClosureScript { base ->
this.configuratorFactories.each { configuratorFactories.each {
it.get().accept(base) it.get().accept(base)
} }
} }
this.builds.addAll(result) this.builds.addAll(result)
} else if (this.buildScript.exists() && this.buildScript.isFile()) { } else if (this.buildScript.exists() && this.buildScript.isFile()) {
logger.info('running buildScript: {}', this.buildScript) logger.info('running buildScript: {}', this.buildScript)
def result = BuildScripts.runBuildScript( def buildScriptRunner = new BuildScriptRunner([
this.buildScript.parentFile.toURI().toURL()
])
this.buildScriptClassLoader = buildScriptRunner.getBuildScriptClassLoader()
def result = buildScriptRunner.runBuildScript(
this.buildScript.name, this.buildScript.name,
this.engine, [args: buildScriptArgs]
[args: this.scriptArgs]
) { base -> ) { base ->
this.configuratorFactories.each { it.get().accept(base) } configuratorFactories.each { it.get().accept(base) }
} }
this.builds.addAll(result) this.builds.addAll(result)
} else { } else {
@ -67,12 +72,27 @@ final class BuildScriptBasedStaticSiteGenerator implements StaticSiteGenerator {
logger.trace(exit, '') logger.trace(exit, '')
} }
/**
* @return The classLoader used to load the buildScript.
* @throws NullPointerException if the buildScriptRunner was not initialized yet (make sure to call
* {@link #doBuild} first).
*/
GroovyClassLoader getBuildScriptClassLoader() {
Objects.requireNonNull(this.buildScriptClassLoader)
}
// TODO: cache
@Override @Override
boolean doBuild(String buildName, Consumer<Collection<Diagnostic>> diagnosticsConsumer = { }) { boolean doBuild(
String buildName,
Collection<BuildScriptConfiguratorFactory> configuratorFactories,
Map<String, Object> buildScriptArgs,
Consumer<Collection<Diagnostic>> diagnosticsConsumer
) {
logger.trace(enter, 'buildName: {}, diagnosticsConsumer: {}', buildName, diagnosticsConsumer) logger.trace(enter, 'buildName: {}, diagnosticsConsumer: {}', buildName, diagnosticsConsumer)
if (!this.ranBuildScript) { if (!this.ranBuildScript) {
this.runBuildScript() this.runBuildScript(configuratorFactories, buildScriptArgs)
} }
def build = this.builds.find { it.name == buildName } def build = this.builds.find { it.name == buildName }

View File

@ -1,9 +1,15 @@
package com.jessebrault.ssg package com.jessebrault.ssg
import com.jessebrault.ssg.buildscript.BuildScriptConfiguratorFactory
import com.jessebrault.ssg.util.Diagnostic import com.jessebrault.ssg.util.Diagnostic
import java.util.function.Consumer import java.util.function.Consumer
interface StaticSiteGenerator { interface StaticSiteGenerator {
boolean doBuild(String buildName, Consumer<Collection<Diagnostic>> diagnosticsConsumer) boolean doBuild(
String buildName,
Collection<BuildScriptConfiguratorFactory> configuratorFactories,
Map<String, Object> buildScriptArgs,
Consumer<Collection<Diagnostic>> diagnosticsConsumer
)
} }

View File

@ -0,0 +1,71 @@
package com.jessebrault.ssg.buildscript
import com.jessebrault.ssg.util.ExtensionUtil
import groovy.transform.NullCheck
import groovy.transform.stc.ClosureParams
import groovy.transform.stc.SimpleType
import org.codehaus.groovy.control.CompilerConfiguration
import java.util.function.Consumer
@NullCheck
final class BuildScriptRunner {
private static Collection<Build> runBase(BuildScriptBase base) {
base.run()
BuildSpecUtil.getBuilds(base.getBuildSpecs())
}
static Collection<Build> runClosureScript(
@DelegatesTo(value = BuildScriptBase, strategy = Closure.DELEGATE_FIRST)
@ClosureParams(value = SimpleType, options = 'com.jessebrault.ssg.buildscript.BuildScriptBase')
Closure<?> scriptBody
) {
def base = new BuildScriptBase() {
@Override
Object run() {
scriptBody.delegate = this
scriptBody.resolveStrategy = Closure.DELEGATE_FIRST
scriptBody.call(this)
}
}
runBase(base)
}
private final GroovyClassLoader buildScriptClassLoader
BuildScriptRunner(Collection<URL> classLoaderUrls) {
this.buildScriptClassLoader = new GroovyClassLoader(
Thread.currentThread().contextClassLoader,
new CompilerConfiguration().tap {
scriptBaseClass = BuildScriptBase.name
}
)
classLoaderUrls.each(this.buildScriptClassLoader::addURL)
}
GroovyClassLoader getBuildScriptClassLoader() {
this.buildScriptClassLoader
}
Collection<Build> runBuildScript(
String scriptName,
Map<String, Object> binding,
Consumer<BuildScriptBase> configureBuildScript
) {
Class<?> scriptClass = this.buildScriptClassLoader.loadClass(
ExtensionUtil.stripExtension(scriptName),
true,
false,
false
)
def scriptObject = scriptClass.getConstructor().newInstance()
assert scriptObject instanceof BuildScriptBase
scriptObject.setBinding(new Binding(binding))
configureBuildScript.accept(scriptObject)
runBase(scriptObject)
}
}

View File

@ -1,99 +0,0 @@
package com.jessebrault.ssg.buildscript
import groovy.transform.stc.ClosureParams
import groovy.transform.stc.SimpleType
import org.codehaus.groovy.control.CompilerConfiguration
import java.util.function.Consumer
final class BuildScripts {
private static Collection<Build> runBase(BuildScriptBase base) {
base.run()
BuildSpecUtil.getBuilds(base.getBuildSpecs())
}
static Collection<Build> runBuildScript(
@DelegatesTo(value = BuildScriptBase, strategy = Closure.DELEGATE_FIRST)
@ClosureParams(value = SimpleType, options = 'com.jessebrault.ssg.buildscript.BuildScriptBase')
Closure<?> scriptBody
) {
def base = new BuildScriptBase() {
@Override
Object run() {
scriptBody.delegate = this
scriptBody.resolveStrategy = Closure.DELEGATE_FIRST
scriptBody.call(this)
}
}
runBase(base)
}
static Collection<Build> runBuildScript(
String scriptName,
GroovyScriptEngine engine,
Map<String, Object> binding = [:],
Consumer<BuildScriptBase> configureBuildScript = { }
) {
engine.config = new CompilerConfiguration().tap {
scriptBaseClass = 'com.jessebrault.ssg.buildscript.BuildScriptBase'
}
def base = engine.createScript(scriptName, new Binding(binding))
assert base instanceof BuildScriptBase
configureBuildScript.accept(base)
runBase(base)
}
@Deprecated
static Collection<Build> runBuildScript(
String scriptName,
URL scriptBaseDirUrl,
Collection<URL> otherUrls,
Map<String, Object> binding,
Consumer<BuildScriptBase> configureBuildScript
) {
def engine = new GroovyScriptEngine([scriptBaseDirUrl, *otherUrls] as URL[])
engine.config = new CompilerConfiguration().tap {
scriptBaseClass = 'com.jessebrault.ssg.buildscript.BuildScriptBase'
}
def base = engine.createScript(scriptName, new Binding(binding))
assert base instanceof BuildScriptBase
configureBuildScript.accept(base)
runBase(base)
}
@Deprecated
static Collection<Build> runBuildScript(
String scriptName,
URL scriptBaseDirUrl,
Collection<URL> otherUrls,
Map<String, Object> binding
) {
runBuildScript(scriptName, scriptBaseDirUrl, otherUrls, binding) { }
}
@Deprecated
static Collection<Build> runBuildScript(
String scriptName,
URL scriptBaseDirUrl,
Collection<URL> otherUrls
) {
runBuildScript(scriptName, scriptBaseDirUrl, otherUrls, [:]) { }
}
@Deprecated
static Collection<Build> runBuildScript(
String scriptName,
URL scriptBaseDirUrl
) {
runBuildScript(scriptName, scriptBaseDirUrl, [], [:]) { }
}
private BuildScripts() {}
}

View File

@ -1,5 +1,6 @@
package com.jessebrault.ssg.buildscript package com.jessebrault.ssg.buildscript
import com.jessebrault.ssg.html.PageToHtmlSpecProviders
import com.jessebrault.ssg.html.PageToHtmlTaskFactory import com.jessebrault.ssg.html.PageToHtmlTaskFactory
import com.jessebrault.ssg.html.TextToHtmlSpecProviders import com.jessebrault.ssg.html.TextToHtmlSpecProviders
import com.jessebrault.ssg.html.TextToHtmlTaskFactory import com.jessebrault.ssg.html.TextToHtmlTaskFactory
@ -16,6 +17,7 @@ import groovy.transform.NullCheck
import groovy.transform.TupleConstructor import groovy.transform.TupleConstructor
import java.util.function.Consumer import java.util.function.Consumer
import java.util.function.Supplier
@TupleConstructor(includeFields = true, defaults = false) @TupleConstructor(includeFields = true, defaults = false)
@NullCheck(includeGenerated = true) @NullCheck(includeGenerated = true)
@ -23,8 +25,7 @@ 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 File tmpDir private final Supplier<ClassLoader> classLoaderSupplier
private final GroovyScriptEngine engine
@Override @Override
Consumer<BuildScriptBase> get() { Consumer<BuildScriptBase> get() {
@ -36,9 +37,9 @@ final class DefaultBuildScriptConfiguratorFactory implements BuildScriptConfigur
types { types {
textTypes << TextTypes.MARKDOWN textTypes << TextTypes.MARKDOWN
pageTypes << PageTypes.getGsp(['.gsp', '.ssg.gst'], this.tmpDir, this.engine) pageTypes << PageTypes.getGsp(['.gsp', '.ssg.gst'], this.classLoaderSupplier.get())
templateTypes << TemplateTypes.getGsp(['.gsp', '.ssg.gst'], this.tmpDir, this.engine) templateTypes << TemplateTypes.getGsp(['.gsp', '.ssg.gst'], this.classLoaderSupplier.get())
partTypes << PartTypes.getGsp(['.gsp', '.ssg.gst'], this.tmpDir, this.engine) partTypes << PartTypes.getGsp(['.gsp', '.ssg.gst'], this.classLoaderSupplier.get())
} }
sources { base, types -> sources { base, types ->
@ -50,14 +51,14 @@ final class DefaultBuildScriptConfiguratorFactory implements BuildScriptConfigur
taskFactories { base, sources -> taskFactories { base, sources ->
register('textToHtml', TextToHtmlTaskFactory::new) { register('textToHtml', TextToHtmlTaskFactory::new) {
it.specProvider += TextToHtmlSpecProviders.from(sources) it.specsProvider += TextToHtmlSpecProviders.from(sources)
it.allTextsProvider += sources.textsProvider it.allTextsProvider += sources.textsProvider
it.allPartsProvider += sources.partsProvider it.allPartsProvider += sources.partsProvider
it.allModelsProvider += sources.modelsProvider it.allModelsProvider += sources.modelsProvider
} }
register('pageToHtml', PageToHtmlTaskFactory::new) { register('pageToHtml', PageToHtmlTaskFactory::new) {
it.pagesProvider += sources.pagesProvider it.specsProvider += PageToHtmlSpecProviders.from(sources.pagesProvider)
it.allTextsProvider += sources.textsProvider it.allTextsProvider += sources.textsProvider
it.allPartsProvider += sources.partsProvider it.allPartsProvider += sources.partsProvider
it.allModelsProvider += sources.modelsProvider it.allModelsProvider += sources.modelsProvider

View File

@ -13,8 +13,7 @@ import groovy.transform.EqualsAndHashCode
import groovy.transform.NullCheck import groovy.transform.NullCheck
import groovy.transform.TupleConstructor import groovy.transform.TupleConstructor
@TupleConstructor(defaults = false) @NullCheck
@NullCheck(includeGenerated = true)
@EqualsAndHashCode @EqualsAndHashCode
final class SourceProviders { final class SourceProviders {
@ -26,22 +25,24 @@ final class SourceProviders {
sp0.modelsProvider + sp1.modelsProvider, sp0.modelsProvider + sp1.modelsProvider,
sp0.pagesProvider + sp1.pagesProvider, sp0.pagesProvider + sp1.pagesProvider,
sp0.templatesProvider + sp1.templatesProvider, sp0.templatesProvider + sp1.templatesProvider,
sp0.partsProvider + sp1.partsProvider sp0.partsProvider + sp1.partsProvider,
sp0.custom + sp1.custom
) )
} }
static SourceProviders get(Map<String, Object> args) { static SourceProviders get(Map<String, Object> args) {
new SourceProviders( new SourceProviders(
args?.textsProvider as CollectionProvider<Text> args.textsProvider as CollectionProvider<Text>
?: CollectionProviders.getEmpty() as CollectionProvider<Text>, ?: CollectionProviders.getEmpty() as CollectionProvider<Text>,
args?.modelsProvider as CollectionProvider<Model<Object>> args.modelsProvider as CollectionProvider<Model<Object>>
?: CollectionProviders.getEmpty() as CollectionProvider<Model<Object>>, ?: CollectionProviders.getEmpty() as CollectionProvider<Model<Object>>,
args?.pagesProvider as CollectionProvider<Page> args.pagesProvider as CollectionProvider<Page>
?: CollectionProviders.getEmpty() as CollectionProvider<Page>, ?: CollectionProviders.getEmpty() as CollectionProvider<Page>,
args?.templatesProvider as CollectionProvider<Template> args.templatesProvider as CollectionProvider<Template>
?: CollectionProviders.getEmpty() as CollectionProvider<Template>, ?: CollectionProviders.getEmpty() as CollectionProvider<Template>,
args?.partsProvider as CollectionProvider<Part> args.partsProvider as CollectionProvider<Part>
?: CollectionProviders.getEmpty() as CollectionProvider<Part> ?: CollectionProviders.getEmpty() as CollectionProvider<Part>,
args.custom as Map<String, CollectionProvider<?>> ?: [:]
) )
} }
@ -51,7 +52,8 @@ final class SourceProviders {
CollectionProviders.getEmpty(), CollectionProviders.getEmpty(),
CollectionProviders.getEmpty(), CollectionProviders.getEmpty(),
CollectionProviders.getEmpty(), CollectionProviders.getEmpty(),
CollectionProviders.getEmpty() CollectionProviders.getEmpty(),
[:]
) )
} }
@ -61,10 +63,40 @@ final class SourceProviders {
final CollectionProvider<Template> templatesProvider final CollectionProvider<Template> templatesProvider
final CollectionProvider<Part> partsProvider final CollectionProvider<Part> partsProvider
private final Map<String, CollectionProvider<Object>> custom
SourceProviders(
CollectionProvider<Text> textsProvider,
CollectionProvider<Model<Object>> modelsProvider,
CollectionProvider<Page> pagesProvider,
CollectionProvider<Template> templatesProvider,
CollectionProvider<Part> partsProvider,
Map<String, CollectionProvider<Object>> custom
) {
this.textsProvider = textsProvider
this.modelsProvider = modelsProvider
this.pagesProvider = pagesProvider
this.templatesProvider = templatesProvider
this.partsProvider = partsProvider
this.custom = custom
}
SourceProviders plus(SourceProviders other) { SourceProviders plus(SourceProviders other) {
concat(this, other) concat(this, other)
} }
def <T> CollectionProvider<T> getCustom(String name, Class<T> tClass) {
this.custom.get(name) as CollectionProvider<T>
}
void putCustom(String name, CollectionProvider<?> customProvider) {
this.custom.put(name, customProvider as CollectionProvider<Object>)
}
Map<String, CollectionProvider<Object>> getAllCustom() {
this.custom
}
@Override @Override
String toString() { String toString() {
"SourceProviders(textsProvider: ${ this.textsProvider }, modelsProvider: ${ this.modelsProvider }, " + "SourceProviders(textsProvider: ${ this.textsProvider }, modelsProvider: ${ this.modelsProvider }, " +

View File

@ -21,6 +21,8 @@ final class SourceProvidersDelegate {
private CollectionProvider<Template> templatesProvider = CollectionProviders.getEmpty() private CollectionProvider<Template> templatesProvider = CollectionProviders.getEmpty()
private CollectionProvider<Part> partsProvider = CollectionProviders.getEmpty() private CollectionProvider<Part> partsProvider = CollectionProviders.getEmpty()
private final Map<String, CollectionProvider<Object>> custom = [:]
void texts(CollectionProvider<Text> textsProvider) { void texts(CollectionProvider<Text> textsProvider) {
this.textsProvider += textsProvider this.textsProvider += textsProvider
} }
@ -41,13 +43,18 @@ final class SourceProvidersDelegate {
this.partsProvider += partsProvider this.partsProvider += partsProvider
} }
void custom(String name, CollectionProvider<?> customProvider) {
this.custom.put(name, customProvider as CollectionProvider<Object>)
}
SourceProviders getResult() { SourceProviders getResult() {
new SourceProviders( new SourceProviders(
this.textsProvider, this.textsProvider,
this.modelsProvider, this.modelsProvider,
this.pagesProvider, this.pagesProvider,
this.templatesProvider, this.templatesProvider,
this.partsProvider this.partsProvider,
this.custom
) )
} }

View File

@ -0,0 +1,17 @@
package com.jessebrault.ssg.html
import com.jessebrault.ssg.page.Page
import com.jessebrault.ssg.task.TaskSpec
import groovy.transform.EqualsAndHashCode
import groovy.transform.NullCheck
import groovy.transform.TupleConstructor
import java.util.function.Function
@TupleConstructor(defaults = false)
@NullCheck(includeGenerated = true)
@EqualsAndHashCode
final class PageToHtmlSpec {
final Page page
final Function<TaskSpec, String> toRelativeHtmlPath
}

View File

@ -0,0 +1,32 @@
package com.jessebrault.ssg.html
import com.jessebrault.ssg.page.Page
import com.jessebrault.ssg.provider.CollectionProvider
import com.jessebrault.ssg.provider.CollectionProviders
import com.jessebrault.ssg.task.TaskSpec
import com.jessebrault.ssg.util.ExtensionUtil
import java.util.function.Function
final class PageToHtmlSpecProviders {
static CollectionProvider<PageToHtmlSpec> from(CollectionProvider<Page> pagesProvider) {
CollectionProviders.fromCollection(pagesProvider.provide().collect { Page page ->
new PageToHtmlSpec(page, { TaskSpec taskSpec ->
ExtensionUtil.stripExtension(page.path) + '.html'
})
})
}
static CollectionProvider<PageToHtmlSpec> from(
CollectionProvider<Page> pagesProvider,
Function<Page, Function<TaskSpec, String>> toRelativeHtmlPath
) {
CollectionProviders.fromCollection(pagesProvider.provide().collect {
new PageToHtmlSpec(it, toRelativeHtmlPath.apply(it))
})
}
private PageToHtmlSpecProviders() {}
}

View File

@ -1,6 +1,6 @@
package com.jessebrault.ssg.html package com.jessebrault.ssg.html
import com.jessebrault.ssg.page.Page
import com.jessebrault.ssg.provider.CollectionProvider import com.jessebrault.ssg.provider.CollectionProvider
import com.jessebrault.ssg.provider.CollectionProviders import com.jessebrault.ssg.provider.CollectionProviders
import com.jessebrault.ssg.task.AbstractRenderTaskFactory import com.jessebrault.ssg.task.AbstractRenderTaskFactory
@ -8,28 +8,27 @@ import com.jessebrault.ssg.task.Task
import com.jessebrault.ssg.task.TaskSpec import com.jessebrault.ssg.task.TaskSpec
import com.jessebrault.ssg.util.Result import com.jessebrault.ssg.util.Result
import static com.jessebrault.ssg.util.ExtensionUtil.stripExtension
import static java.util.Objects.requireNonNull import static java.util.Objects.requireNonNull
final class PageToHtmlTaskFactory extends AbstractRenderTaskFactory { final class PageToHtmlTaskFactory extends AbstractRenderTaskFactory {
CollectionProvider<Page> pagesProvider = CollectionProviders.getEmpty() CollectionProvider<PageToHtmlSpec> specsProvider = CollectionProviders.getEmpty()
@Override @Override
Result<Collection<Task>> getTasks(TaskSpec taskSpec) { Result<Collection<Task>> getTasks(TaskSpec taskSpec) {
super.checkProviders() this.checkProviders()
requireNonNull(this.pagesProvider) requireNonNull(this.specsProvider)
def allTexts = this.allTextsProvider.provide() def allTexts = this.allTextsProvider.provide()
def allModels = this.allModelsProvider.provide() def allModels = this.allModelsProvider.provide()
def allParts = this.allPartsProvider.provide() def allParts = this.allPartsProvider.provide()
final Collection<Task> tasks = this.pagesProvider.provide() final Collection<Task> tasks = this.specsProvider.provide()
.collect { .collect {
new PageToHtmlTask( new PageToHtmlTask(
stripExtension(it.path) + '.html', it.toRelativeHtmlPath.apply(taskSpec),
taskSpec, taskSpec,
it, it.page,
allTexts, allTexts,
allModels, allModels,
allParts allParts

View File

@ -1,16 +1,19 @@
package com.jessebrault.ssg.html package com.jessebrault.ssg.html
import com.jessebrault.ssg.task.TaskSpec
import com.jessebrault.ssg.template.Template import com.jessebrault.ssg.template.Template
import com.jessebrault.ssg.text.Text import com.jessebrault.ssg.text.Text
import groovy.transform.EqualsAndHashCode import groovy.transform.EqualsAndHashCode
import groovy.transform.NullCheck import groovy.transform.NullCheck
import groovy.transform.TupleConstructor import groovy.transform.TupleConstructor
import java.util.function.Function
@TupleConstructor(defaults = false) @TupleConstructor(defaults = false)
@NullCheck(includeGenerated = true) @NullCheck(includeGenerated = true)
@EqualsAndHashCode @EqualsAndHashCode
final class TextToHtmlSpec { final class TextToHtmlSpec {
final Text text final Text text
final Template template final Template template
final String path final Function<TaskSpec, String> toRelativeHtmlPath
} }

View File

@ -3,15 +3,39 @@ package com.jessebrault.ssg.html
import com.jessebrault.ssg.buildscript.SourceProviders import com.jessebrault.ssg.buildscript.SourceProviders
import com.jessebrault.ssg.provider.CollectionProvider import com.jessebrault.ssg.provider.CollectionProvider
import com.jessebrault.ssg.provider.CollectionProviders import com.jessebrault.ssg.provider.CollectionProviders
import com.jessebrault.ssg.task.TaskSpec
import com.jessebrault.ssg.template.Template
import com.jessebrault.ssg.text.Text
import com.jessebrault.ssg.util.ExtensionUtil import com.jessebrault.ssg.util.ExtensionUtil
import com.jessebrault.ssg.util.Result import com.jessebrault.ssg.util.Result
import java.util.function.Function
final class TextToHtmlSpecProviders { final class TextToHtmlSpecProviders {
static CollectionProvider<Result<TextToHtmlSpec>> from(SourceProviders sources) { static CollectionProvider<Result<TextToHtmlSpec>> from(SourceProviders sources) {
from(sources) { text ->
return { TaskSpec taskSpec ->
ExtensionUtil.stripExtension(text.path) + '.html'
}
}
}
static CollectionProvider<Result<TextToHtmlSpec>> from(
SourceProviders sources,
Function<Text, Function<TaskSpec, String>> toRelativeHtmlPath
) {
from(sources.textsProvider, sources.templatesProvider, toRelativeHtmlPath)
}
static CollectionProvider<Result<TextToHtmlSpec>> from(
CollectionProvider<Text> textsProvider,
CollectionProvider<Template> templatesProvider,
Function<Text, Function<TaskSpec, String>> toRelativeHtmlPath
) {
CollectionProviders.fromSupplier { CollectionProviders.fromSupplier {
def templates = sources.templatesProvider.provide() def templates = templatesProvider.provide()
sources.textsProvider.provide().findResults { textsProvider.provide().findResults {
def frontMatterResult = it.type.frontMatterGetter.get(it) def frontMatterResult = it.type.frontMatterGetter.get(it)
if (frontMatterResult.hasDiagnostics()) { if (frontMatterResult.hasDiagnostics()) {
return Result.ofDiagnostics(frontMatterResult.diagnostics) as Result<TextToHtmlSpec> return Result.ofDiagnostics(frontMatterResult.diagnostics) as Result<TextToHtmlSpec>
@ -22,7 +46,7 @@ final class TextToHtmlSpecProviders {
return Result.of(new TextToHtmlSpec( return Result.of(new TextToHtmlSpec(
it, it,
template, template,
ExtensionUtil.stripExtension(it.path) + '.html' toRelativeHtmlPath.apply(it)
)) ))
} else { } else {
return null return null

View File

@ -12,12 +12,12 @@ import static java.util.Objects.requireNonNull
final class TextToHtmlTaskFactory extends AbstractRenderTaskFactory { final class TextToHtmlTaskFactory extends AbstractRenderTaskFactory {
CollectionProvider<Result<TextToHtmlSpec>> specProvider = CollectionProviders.getEmpty() CollectionProvider<Result<TextToHtmlSpec>> specsProvider = CollectionProviders.getEmpty()
@Override @Override
Result<Collection<Task>> getTasks(TaskSpec taskSpec) { Result<Collection<Task>> getTasks(TaskSpec taskSpec) {
super.checkProviders() super.checkProviders()
requireNonNull(this.specProvider) requireNonNull(this.specsProvider)
def allTexts = this.allTextsProvider.provide() def allTexts = this.allTextsProvider.provide()
def allModels = this.allModelsProvider.provide() def allModels = this.allModelsProvider.provide()
@ -25,23 +25,22 @@ final class TextToHtmlTaskFactory extends AbstractRenderTaskFactory {
Collection<Diagnostic> diagnostics = [] Collection<Diagnostic> diagnostics = []
final Collection<Task> tasks = this.specProvider.provide() final Collection<Task> tasks = this.specsProvider.provide().findResults {
.findResults { if (it.hasDiagnostics()) {
if (it.hasDiagnostics()) { diagnostics.addAll(it.diagnostics)
diagnostics.addAll(it.diagnostics) } else {
} else { def spec = it.get()
def spec = it.get() new TextToHtmlTask(
new TextToHtmlTask( spec.toRelativeHtmlPath.apply(taskSpec),
spec.path, taskSpec,
taskSpec, spec.text,
spec.text, spec.template,
spec.template, allTexts,
allTexts, allModels,
allModels, allParts
allParts )
) }
} }
}
Result.of(diagnostics, tasks) Result.of(diagnostics, tasks)
} }

View File

@ -2,7 +2,6 @@ package com.jessebrault.ssg.page
import com.jessebrault.ssg.render.RenderContext import com.jessebrault.ssg.render.RenderContext
import com.jessebrault.ssg.render.StandardGspRenderer import com.jessebrault.ssg.render.StandardGspRenderer
import com.jessebrault.ssg.util.Diagnostic
import com.jessebrault.ssg.util.Result import com.jessebrault.ssg.util.Result
import groovy.transform.EqualsAndHashCode import groovy.transform.EqualsAndHashCode
import groovy.transform.NullCheck import groovy.transform.NullCheck
@ -13,8 +12,8 @@ final class GspPageRenderer implements PageRenderer {
private final StandardGspRenderer gspRenderer private final StandardGspRenderer gspRenderer
GspPageRenderer(File tmpDir, GroovyScriptEngine engine) { GspPageRenderer(ClassLoader parentClassLoader) {
this.gspRenderer = new StandardGspRenderer(tmpDir, engine) this.gspRenderer = new StandardGspRenderer(parentClassLoader)
} }
@Override @Override

View File

@ -1,9 +1,12 @@
package com.jessebrault.ssg.page package com.jessebrault.ssg.page
import groovy.transform.NullCheck
@NullCheck
final class PageTypes { final class PageTypes {
static PageType getGsp(Collection<String> extensions, File tmpDir, GroovyScriptEngine engine) { static PageType getGsp(Collection<String> extensions, ClassLoader parentClassLoader) {
new PageType(extensions, new GspPageRenderer(tmpDir, engine)) new PageType(extensions, new GspPageRenderer(parentClassLoader))
} }
private PageTypes() {} private PageTypes() {}

View File

@ -6,23 +6,34 @@ import com.jessebrault.ssg.util.ExtensionUtil
import org.slf4j.Logger import org.slf4j.Logger
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import java.util.function.BiPredicate
import java.util.function.Predicate
final class PagesProviders { final class PagesProviders {
private static final Logger logger = LoggerFactory.getLogger(PagesProviders) private static final Logger logger = LoggerFactory.getLogger(PagesProviders)
static CollectionProvider<Page> from(File pagesDirectory, Collection<PageType> pageTypes) { static CollectionProvider<Page> from(File pagesDirectory, Collection<PageType> pageTypes) {
from(pagesDirectory, pageTypes) { file, path -> true }
}
static CollectionProvider<Page> from(File pagesDirectory, Collection<PageType> pageTypes, BiPredicate<File, String> filter) {
CollectionProviders.fromDirectory(pagesDirectory) { file, relativePath -> CollectionProviders.fromDirectory(pagesDirectory) { file, relativePath ->
def extension = ExtensionUtil.getExtension(relativePath) if (filter.test(file, relativePath)) {
if (extension) { def extension = ExtensionUtil.getExtension(relativePath)
def pageType = pageTypes.find { it.ids.contains(extension) } if (extension) {
if (!pageType) { def pageType = pageTypes.find { it.ids.contains(extension) }
logger.debug('there is no PageType for file {}; skipping', file) if (!pageType) {
return null logger.debug('there is no PageType for file {}; skipping', file)
return null
} else {
return new Page(relativePath, pageType, file.getText())
}
} else { } else {
return new Page(relativePath, pageType, file.getText()) logger.debug('there is no extension for file {}; skipping', file)
return null
} }
} else { } else {
logger.debug('there is no extension for file {}; skipping', file)
return null return null
} }
} }

View File

@ -3,7 +3,6 @@ package com.jessebrault.ssg.part
import com.jessebrault.ssg.render.RenderContext import com.jessebrault.ssg.render.RenderContext
import com.jessebrault.ssg.render.StandardGspRenderer import com.jessebrault.ssg.render.StandardGspRenderer
import com.jessebrault.ssg.text.Text import com.jessebrault.ssg.text.Text
import com.jessebrault.ssg.util.Diagnostic
import com.jessebrault.ssg.util.Result import com.jessebrault.ssg.util.Result
import groovy.transform.EqualsAndHashCode import groovy.transform.EqualsAndHashCode
import org.jetbrains.annotations.Nullable import org.jetbrains.annotations.Nullable
@ -15,8 +14,8 @@ final class GspPartRenderer implements PartRenderer {
private final StandardGspRenderer gspRenderer private final StandardGspRenderer gspRenderer
GspPartRenderer(File tmpDir, GroovyScriptEngine engine) { GspPartRenderer(ClassLoader parentClassLoader) {
this.gspRenderer = new StandardGspRenderer(tmpDir, engine) this.gspRenderer = new StandardGspRenderer(parentClassLoader)
} }
@Override @Override

View File

@ -1,9 +1,12 @@
package com.jessebrault.ssg.part package com.jessebrault.ssg.part
import groovy.transform.NullCheck
@NullCheck
final class PartTypes { final class PartTypes {
static PartType getGsp(Collection<String> extensions, File tmpDir, GroovyScriptEngine engine) { static PartType getGsp(Collection<String> extensions, ClassLoader parentClassLoader) {
new PartType(extensions, new GspPartRenderer(tmpDir, engine)) new PartType(extensions, new GspPartRenderer(parentClassLoader))
} }
private PartTypes() {} private PartTypes() {}

View File

@ -13,8 +13,8 @@ final class StandardGspRenderer {
private final TemplateCreator templateCreator private final TemplateCreator templateCreator
StandardGspRenderer(File tmpDir, GroovyScriptEngine engine) { StandardGspRenderer(ClassLoader parentClassLoader) {
this.templateCreator = new GroovyTemplateCreator(ExtendedGstParser::new, tmpDir, engine, true) this.templateCreator = new GroovyTemplateCreator(ExtendedGstParser::new, parentClassLoader, true)
} }
Result<String> render( Result<String> render(

View File

@ -1,7 +1,6 @@
package com.jessebrault.ssg.template package com.jessebrault.ssg.template
import com.jessebrault.ssg.render.StandardGspRenderer import com.jessebrault.ssg.render.StandardGspRenderer
import com.jessebrault.ssg.util.Diagnostic
import com.jessebrault.ssg.render.RenderContext import com.jessebrault.ssg.render.RenderContext
import com.jessebrault.ssg.util.Result import com.jessebrault.ssg.util.Result
import com.jessebrault.ssg.text.Text import com.jessebrault.ssg.text.Text
@ -14,8 +13,8 @@ final class GspTemplateRenderer implements TemplateRenderer {
private final StandardGspRenderer gspRenderer private final StandardGspRenderer gspRenderer
GspTemplateRenderer(File tmpDir, GroovyScriptEngine engine) { GspTemplateRenderer(ClassLoader parentClassLoader) {
this.gspRenderer = new StandardGspRenderer(tmpDir, engine) this.gspRenderer = new StandardGspRenderer(parentClassLoader)
} }
@Override @Override

View File

@ -1,9 +1,12 @@
package com.jessebrault.ssg.template package com.jessebrault.ssg.template
import groovy.transform.NullCheck
@NullCheck
final class TemplateTypes { final class TemplateTypes {
static TemplateType getGsp(Collection<String> extensions, File tmpDir, GroovyScriptEngine engine) { static TemplateType getGsp(Collection<String> extensions, ClassLoader parentClassLoader) {
new TemplateType(extensions, new GspTemplateRenderer(tmpDir, engine)) new TemplateType(extensions, new GspTemplateRenderer(parentClassLoader))
} }
private TemplateTypes() {} private TemplateTypes() {}

View File

@ -1,14 +1,10 @@
package com.jessebrault.ssg package com.jessebrault.ssg
import com.jessebrault.ssg.buildscript.BuildScriptBase
import com.jessebrault.ssg.buildscript.BuildScriptConfiguratorFactory
import com.jessebrault.ssg.util.ResourceUtil import com.jessebrault.ssg.util.ResourceUtil
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
import org.slf4j.Logger import org.slf4j.Logger
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import java.util.function.Consumer
import static com.jessebrault.ssg.util.FileAssertions.assertFileStructureAndContents import static com.jessebrault.ssg.util.FileAssertions.assertFileStructureAndContents
import static org.junit.jupiter.api.Assertions.assertTrue import static org.junit.jupiter.api.Assertions.assertTrue
@ -32,19 +28,17 @@ final class BuildScriptBasedStaticSiteGeneratorTests {
} }
} }
def tmpDir = File.createTempDir() def ssg = new BuildScriptBasedStaticSiteGenerator([sourceDir.toURI().toURL()], buildScript)
def engine = new GroovyScriptEngine([sourceDir.toURI().toURL(), tmpDir.toURI().toURL()] as URL[])
def ssg = new BuildScriptBasedStaticSiteGenerator(engine, [new BuildScriptConfiguratorFactory() {
@Override assertTrue(
Consumer<BuildScriptBase> get() { ssg.doBuild(
return { } 'test',
} [],
[sourceDir: sourceDir, gclSupplier: ssg::getBuildScriptClassLoader]
}], buildScript, [sourceDir: sourceDir, tmpDir: tmpDir, engine: engine]) ) {
assertTrue(ssg.doBuild('test') { it.each { logger.error(it.toString()) }
it.each { logger.error(it.toString()) } }
}) )
def expectedBase = File.createTempDir() def expectedBase = File.createTempDir()
new File(expectedBase, 'hello.html').tap { new File(expectedBase, 'hello.html').tap {

View File

@ -10,189 +10,179 @@ import org.mockito.junit.jupiter.MockitoExtension
import java.util.function.Consumer import java.util.function.Consumer
import java.util.function.Function import java.util.function.Function
import static com.jessebrault.ssg.buildscript.BuildScripts.runBuildScript import static BuildScriptRunner.runClosureScript
import static org.junit.jupiter.api.Assertions.assertEquals import static org.junit.jupiter.api.Assertions.assertEquals
import static org.mockito.Mockito.verify import static org.mockito.Mockito.verify
@NullCheck
@ExtendWith(MockitoExtension)
final class BuildScriptsTests { final class BuildScriptsTests {
@NullCheck /**
@ExtendWith(MockitoExtension) * Must be non-static, otherwise Groovy gets confused inside the Closures.
static final class ScriptFileTests { *
* TODO: use the FileUtil.copyResourceToWriter method
/** */
* Must be non-static, otherwise Groovy gets confused inside the Closures. @SuppressWarnings('GrMethodMayBeStatic')
* private void copyLocalResourceToWriter(String name, Writer target) {
* TODO: use the FileUtil.copyResourceToWriter method BuildScriptsTests.getResourceAsStream(name).withReader {
*/ it.transferTo(target)
@SuppressWarnings('GrMethodMayBeStatic')
private void copyLocalResourceToWriter(String name, Writer target) {
BuildScriptsTests.getResourceAsStream(name).withReader {
it.transferTo(target)
}
} }
/**
* Must be non-static, otherwise Groovy gets confused inside the Closures.
*/
@SuppressWarnings('GrMethodMayBeStatic')
private File setupScripts(Collection<String> resourceNames) {
def tempDir = File.createTempDir()
new FileTreeBuilder(tempDir).tap {
resourceNames.each { String resourceName ->
file(resourceName).withWriter {
this.copyLocalResourceToWriter(resourceName, it)
}
}
}
tempDir
}
private static Collection<Build> getBuilds(
String scriptName,
Collection<URL> urls,
Map<String, Object> binding = [:],
Consumer<BuildScriptBase> configureBase = { }
) {
runBuildScript(scriptName, new GroovyScriptEngine(urls as URL[]), binding, configureBase)
}
@Test
void simpleScript() {
def baseDir = this.setupScripts(['simple.groovy'])
def builds = getBuilds('simple.groovy', [baseDir.toURI().toURL()])
assertEquals(1, builds.size())
assertEquals('test', builds[0].name)
}
@Test
void testImport() {
def baseDir = this.setupScripts(['testImport.groovy', 'TestHtmlTask.groovy'])
def builds = getBuilds('testImport.groovy', [baseDir.toURI().toURL()])
assertEquals(1, builds.size())
assertEquals('test', builds[0].name)
}
@Test
void buildSrcTest() {
def baseDir = File.createTempDir()
new FileTreeBuilder(baseDir).tap {
file('buildSrcTest.groovy').withWriter {
this.copyLocalResourceToWriter('buildSrcTest.groovy', it)
}
dir('buildSrc') {
file('AnotherTask.groovy').withWriter {
this.copyLocalResourceToWriter('buildSrc/AnotherTask.groovy', it)
}
}
}
def builds = getBuilds(
'buildSrcTest.groovy',
[
baseDir.toURI().toURL(),
new File(baseDir, 'buildSrc').toURI().toURL()
]
)
assertEquals(1, builds.size())
assertEquals('test', builds[0].name)
}
@Test
void withBinding(@Mock Consumer<String> stringConsumer) {
def baseDir = this.setupScripts(['withBinding.groovy'])
def engine = new GroovyScriptEngine([baseDir.toURI().toURL()] as URL[])
runBuildScript('withBinding.groovy', engine, [stringConsumer: stringConsumer])
verify(stringConsumer).accept('test')
}
} }
@ExtendWith(MockitoExtension) /**
static final class ClosureScriptTests { * Must be non-static, otherwise Groovy gets confused inside the Closures.
*/
@Test @SuppressWarnings('GrMethodMayBeStatic')
void simple() { private File setupScripts(Collection<String> resourceNames) {
def result = runBuildScript { def tempDir = File.createTempDir()
build(name: 'test') { } new FileTreeBuilder(tempDir).tap {
} resourceNames.each { String resourceName ->
assertEquals(1, result.size()) file(resourceName).withWriter {
assertEquals('test', result[0].name) this.copyLocalResourceToWriter(resourceName, it)
}
@Test
void oneBuildOutputDirWithFunction(@Mock Function<Build, OutputDir> mockOutputDirFunction) {
def r = runBuildScript {
build(name: 'test') {
outputDirFunction = mockOutputDirFunction
} }
} }
assertEquals(1, r.size())
def b0 = r[0]
assertEquals(mockOutputDirFunction, b0.outputDirFunction)
} }
tempDir
}
@Test private static Collection<Build> getBuilds(
void oneBuildOutputDirWithFile() { String scriptName,
def f = new File('test') Collection<URL> urls,
def r = runBuildScript { Map<String, Object> binding = [:],
build(name: 'test') { Consumer<BuildScriptBase> configureBase = { }
outputDir = f ) {
new BuildScriptRunner(urls).runBuildScript(scriptName, binding, configureBase)
}
@Test
void simpleScript() {
def baseDir = this.setupScripts(['simple.groovy'])
def builds = getBuilds('simple.groovy', [baseDir.toURI().toURL()])
assertEquals(1, builds.size())
assertEquals('test', builds[0].name)
}
@Test
void testImport() {
def baseDir = this.setupScripts(['testImport.groovy', 'TestHtmlTask.groovy'])
def builds = getBuilds('testImport.groovy', [baseDir.toURI().toURL()])
assertEquals(1, builds.size())
assertEquals('test', builds[0].name)
}
@Test
void buildSrcTest() {
def baseDir = File.createTempDir()
new FileTreeBuilder(baseDir).tap {
file('buildSrcTest.groovy').withWriter {
this.copyLocalResourceToWriter('buildSrcTest.groovy', it)
}
dir('buildSrc') {
file('AnotherTask.groovy').withWriter {
this.copyLocalResourceToWriter('buildSrc/AnotherTask.groovy', it)
} }
} }
assertEquals(1, r.size())
def b0 = r[0]
assertEquals(f, b0.outputDirFunction.apply(b0) as File)
} }
def builds = getBuilds(
'buildSrcTest.groovy',
[
baseDir.toURI().toURL(),
new File(baseDir, 'buildSrc').toURI().toURL()
]
)
assertEquals(1, builds.size())
assertEquals('test', builds[0].name)
}
@Test @Test
void oneBuildOutputDirWithString() { void withBinding(@Mock Consumer<String> stringConsumer) {
def r = runBuildScript { def baseDir = this.setupScripts(['withBinding.groovy'])
build(name: 'test') { getBuilds('withBinding.groovy', [baseDir.toURI().toURL()], [stringConsumer: stringConsumer])
outputDir = 'test' verify(stringConsumer).accept('test')
}
@Test
void simple() {
def result = runClosureScript {
build(name: 'test') { }
}
assertEquals(1, result.size())
assertEquals('test', result[0].name)
}
@Test
void oneBuildOutputDirWithFunction(@Mock Function<Build, OutputDir> mockOutputDirFunction) {
def r = runClosureScript {
build(name: 'test') {
outputDirFunction = mockOutputDirFunction
}
}
assertEquals(1, r.size())
def b0 = r[0]
assertEquals(mockOutputDirFunction, b0.outputDirFunction)
}
@Test
void oneBuildOutputDirWithFile() {
def f = new File('test')
def r = runClosureScript {
build(name: 'test') {
outputDir = f
}
}
assertEquals(1, r.size())
def b0 = r[0]
assertEquals(f, b0.outputDirFunction.apply(b0) as File)
}
@Test
void oneBuildOutputDirWithString() {
def r = runClosureScript {
build(name: 'test') {
outputDir = 'test'
}
}
assertEquals(1, r.size())
def b0 = r[0]
assertEquals('test', b0.outputDirFunction.apply(b0) as String)
}
@Test
void oneBuildSiteSpec() {
def r = runClosureScript {
build(name: 'test') {
siteSpec {
name = 'testSite'
baseUrl = 'https://testsite.com'
} }
} }
assertEquals(1, r.size())
def b0 = r[0]
assertEquals('test', b0.outputDirFunction.apply(b0) as String)
} }
assertEquals(1, r.size())
def b0 = r[0]
assertEquals(new SiteSpec('testSite', 'https://testsite.com'), b0.siteSpec)
}
@Test @Test
void oneBuildSiteSpec() { void oneBuildWithAbstractParent() {
def r = runBuildScript { def r = runClosureScript {
build(name: 'test') { abstractBuild(name: 'parent') {
siteSpec { siteSpec {
name = 'testSite' name = 'Test Site'
baseUrl = 'https://testsite.com' baseUrl = 'https://test.com'
}
}
}
assertEquals(1, r.size())
def b0 = r[0]
assertEquals(new SiteSpec('testSite', 'https://testsite.com'), b0.siteSpec)
}
@Test
void oneBuildWithAbstractParent() {
def r = runBuildScript {
abstractBuild(name: 'parent') {
siteSpec {
name = 'Test Site'
baseUrl = 'https://test.com'
}
}
build(name: 'child', extending: 'parent') {
siteSpec { base ->
baseUrl = base.baseUrl + '/child'
}
} }
} }
assertEquals(1, r.size()) build(name: 'child', extending: 'parent') {
def b0 = r[0] siteSpec { base ->
assertEquals(new SiteSpec('Test Site', 'https://test.com/child'), b0.siteSpec) baseUrl = base.baseUrl + '/child'
}
}
} }
assertEquals(1, r.size())
def b0 = r[0]
assertEquals(new SiteSpec('Test Site', 'https://test.com/child'), b0.siteSpec)
} }
} }

View File

@ -7,17 +7,14 @@ import com.jessebrault.ssg.util.Result
final class GspPageRendererTests implements StandardDslConsumerTests { final class GspPageRendererTests implements StandardDslConsumerTests {
private static GspPageRenderer getRenderer( private static GspPageRenderer getRenderer(
ClassLoader classLoader, ClassLoader parentClassLoader = GspPageRendererTests.classLoader
Collection<URL> urls
) { ) {
def tmpDir = File.createTempDir() new GspPageRenderer(parentClassLoader)
def engine = new GroovyScriptEngine([tmpDir.toURI().toURL(), *urls] as URL[], classLoader)
new GspPageRenderer(tmpDir, engine)
} }
@Override @Override
Result<String> render(String scriptlet, RenderContext context, ClassLoader classLoader, Collection<URL> urls) { Result<String> render(String scriptlet, RenderContext context, ClassLoader classLoader) {
def renderer = getRenderer(classLoader, urls) def renderer = getRenderer(classLoader)
renderer.render( renderer.render(
new Page('', new PageType([], renderer), scriptlet), new Page('', new PageType([], renderer), scriptlet),
context context

View File

@ -14,13 +14,8 @@ import static org.junit.jupiter.api.Assertions.assertTrue
final class GspPartRendererTests implements StandardDslConsumerTests { final class GspPartRendererTests implements StandardDslConsumerTests {
private static GspPartRenderer getRenderer( private static GspPartRenderer getRenderer(ClassLoader parentClassLoader = GspPartRendererTests.classLoader) {
ClassLoader parentClassLoader = GspPartRendererTests.classLoader, new GspPartRenderer(parentClassLoader)
Collection<URL> urls = []
) {
def tmpDir = File.createTempDir()
def engine = new GroovyScriptEngine([tmpDir.toURI().toURL(), *urls] as URL[], parentClassLoader)
new GspPartRenderer(tmpDir, engine)
} }
private static Result<String> doRender( private static Result<String> doRender(
@ -28,10 +23,9 @@ final class GspPartRendererTests implements StandardDslConsumerTests {
RenderContext context, RenderContext context,
Map binding = [:], Map binding = [:],
@Nullable Text text = null, @Nullable Text text = null,
ClassLoader classLoader = GspPartRendererTests.classLoader, ClassLoader classLoader = GspPartRendererTests.classLoader
Collection<URL> urls = []
) { ) {
def renderer = getRenderer(classLoader, urls) def renderer = getRenderer(classLoader)
renderer.render( renderer.render(
new Part('', new PartType([], renderer), scriptlet), new Part('', new PartType([], renderer), scriptlet),
binding, binding,
@ -41,8 +35,8 @@ final class GspPartRendererTests implements StandardDslConsumerTests {
} }
@Override @Override
Result<String> render(String scriptlet, RenderContext context, ClassLoader classLoader, Collection<URL> urls) { Result<String> render(String scriptlet, RenderContext context, ClassLoader classLoader) {
doRender(scriptlet, context, [:], null, classLoader, urls) doRender(scriptlet, context, [:], null, classLoader)
} }
@Test @Test

View File

@ -13,29 +13,23 @@ import static org.junit.jupiter.api.Assertions.assertEquals
final class GspTemplateRendererTests implements StandardDslConsumerTests { final class GspTemplateRendererTests implements StandardDslConsumerTests {
private static TemplateRenderer getRenderer( private static TemplateRenderer getRenderer(ClassLoader parentClassLoader = GspTemplateRendererTests.classLoader) {
ClassLoader parentLoader = GspTemplateRendererTests.class.classLoader, new GspTemplateRenderer(parentClassLoader)
Collection<URL> urls = []
) {
def tmpDir = File.createTempDir()
def engine = new GroovyScriptEngine([tmpDir.toURI().toURL(), *urls] as URL[], parentLoader)
new GspTemplateRenderer(tmpDir, engine)
} }
private static Result<String> doRender( private static Result<String> doRender(
String scriptlet, String scriptlet,
Text text, Text text,
RenderContext context, RenderContext context,
ClassLoader classLoader, ClassLoader classLoader
Collection<URL> urls
) { ) {
def renderer = getRenderer(classLoader, urls) def renderer = getRenderer(classLoader)
renderer.render(new Template('', new TemplateType([], renderer), scriptlet), text, context) renderer.render(new Template('', new TemplateType([], renderer), scriptlet), text, context)
} }
@Override @Override
Result<String> render(String scriptlet, RenderContext context, ClassLoader classLoader, Collection<URL> urls) { Result<String> render(String scriptlet, RenderContext context, ClassLoader classLoader) {
doRender(scriptlet, blankText(), context, classLoader, urls) doRender(scriptlet, blankText(), context, classLoader)
} }
@Test @Test

View File

@ -9,13 +9,14 @@ import com.jessebrault.ssg.text.TextTypes
import com.jessebrault.ssg.text.TextsProviders import com.jessebrault.ssg.text.TextsProviders
import groovy.transform.BaseScript import groovy.transform.BaseScript
import java.util.function.Supplier
@BaseScript @BaseScript
BuildScriptBase b BuildScriptBase b
final class Args { final class Args {
File sourceDir File sourceDir
File tmpDir Supplier<GroovyClassLoader> gclSupplier
GroovyScriptEngine engine
} }
def args = args as Args def args = args as Args
@ -25,7 +26,7 @@ build(name: 'test') {
types { types {
textTypes << TextTypes.MARKDOWN textTypes << TextTypes.MARKDOWN
templateTypes << TemplateTypes.getGsp(['.gsp'], args.tmpDir, args.engine) templateTypes << TemplateTypes.getGsp(['.gsp'], args.gclSupplier.get())
} }
sources { base, types -> sources { base, types ->
@ -35,7 +36,7 @@ build(name: 'test') {
taskFactories { base, sources -> taskFactories { base, sources ->
register('textToHtml', TextToHtmlTaskFactory::new) { register('textToHtml', TextToHtmlTaskFactory::new) {
it.specProvider += TextToHtmlSpecProviders.from(sources) it.specsProvider += TextToHtmlSpecProviders.from(sources)
} }
} }
} }

View File

@ -33,7 +33,7 @@ import static org.mockito.Mockito.*
@ExtendWith(MockitoExtension) @ExtendWith(MockitoExtension)
interface StandardDslConsumerTests { interface StandardDslConsumerTests {
Result<String> render(String scriptlet, RenderContext context, ClassLoader classLoader, Collection<URL> urls) Result<String> render(String scriptlet, RenderContext context, ClassLoader classLoader)
default void checkResult(String expected, Result<String> result) { default void checkResult(String expected, Result<String> result) {
assertEmptyDiagnostics(result) assertEmptyDiagnostics(result)
@ -44,21 +44,19 @@ interface StandardDslConsumerTests {
String expected, String expected,
String scriptlet, String scriptlet,
RenderContext context = new RenderContext(), RenderContext context = new RenderContext(),
ClassLoader classLoader = this.class.classLoader, ClassLoader classLoader = this.class.classLoader
Collection<URL> urls = []
) { ) {
this.checkResult(expected, this.render(scriptlet, context, classLoader, urls)) this.checkResult(expected, this.render(scriptlet, context, classLoader))
} }
default void doDslAssertionTest( default void doDslAssertionTest(
String scriptlet, String scriptlet,
RenderContext context = new RenderContext(), RenderContext context = new RenderContext(),
ClassLoader classLoader = this.class.classLoader, ClassLoader classLoader = this.class.classLoader
Collection<URL> urls = []
) { ) {
Result<String> result = null Result<String> result = null
try { try {
result = this.render(scriptlet, context, classLoader, urls) result = this.render(scriptlet, context, classLoader)
} catch (Throwable e) { } catch (Throwable e) {
fail(e) fail(e)
} }
@ -279,12 +277,13 @@ interface StandardDslConsumerTests {
['com', 'jessebrault', 'ssg', 'tmp'], ['com', 'jessebrault', 'ssg', 'tmp'],
greeterText greeterText
) )
def configuredClassLoader = new GroovyClassLoader(this.class.classLoader)
configuredClassLoader.addURL(greeterBaseUrl)
this.doDslRenderTest( this.doDslRenderTest(
"Hello, World!", "Hello, World!",
"<%@ import com.jessebrault.ssg.tmp.TmpGreeter %><%= new TmpGreeter().greet() %>", "<%@ import com.jessebrault.ssg.tmp.TmpGreeter %><%= new TmpGreeter().greet() %>",
new RenderContext(), new RenderContext(),
parentClassLoader, configuredClassLoader
[greeterBaseUrl]
) )
} }

View File

@ -1,5 +1,6 @@
package com.jessebrault.ssg package com.jessebrault.ssg
import com.jessebrault.ssg.buildscript.BuildScriptConfiguratorFactory
import com.jessebrault.ssg.util.Diagnostic import com.jessebrault.ssg.util.Diagnostic
import org.apache.logging.log4j.LogManager import org.apache.logging.log4j.LogManager
import org.apache.logging.log4j.Logger import org.apache.logging.log4j.Logger
@ -37,23 +38,32 @@ abstract class AbstractBuildCommand extends AbstractSubCommand {
) )
Collection<String> requestedBuilds = ['default'] Collection<String> requestedBuilds = ['default']
protected StaticSiteGenerator staticSiteGenerator = null protected CliBasedStaticSiteGenerator staticSiteGenerator = null
protected final Integer doSingleBuild(String requestedBuild, File tmpDir, GroovyScriptEngine engine) { protected final Integer doSingleBuild(
String requestedBuild,
Collection<BuildScriptConfiguratorFactory> configuratorFactories,
Map<String, Object> buildScriptBinding
) {
logger.traceEntry('requestedBuild: {}', requestedBuild) logger.traceEntry('requestedBuild: {}', requestedBuild)
if (this.staticSiteGenerator == null) { if (this.staticSiteGenerator == null) {
this.staticSiteGenerator = new CliBasedStaticSiteGenerator( this.staticSiteGenerator = new CliBasedStaticSiteGenerator(
new File('.'),
this.buildScript, this.buildScript,
tmpDir, [
engine, this.buildScript.parentFile.toURI().toURL(),
this.scriptArgs *this.buildSrcDirs.collect { it.toURI().toURL() }
]
) )
} }
final Collection<Diagnostic> diagnostics = [] final Collection<Diagnostic> diagnostics = []
if (!this.staticSiteGenerator.doBuild(requestedBuild, diagnostics.&addAll)) { if (!this.staticSiteGenerator.doBuild(
requestedBuild,
configuratorFactories,
buildScriptBinding,
diagnostics.&addAll
)) {
diagnostics.each { logger.warn(it) } diagnostics.each { logger.warn(it) }
logger.traceExit(1) logger.traceExit(1)
} else { } else {

View File

@ -1,6 +1,6 @@
package com.jessebrault.ssg package com.jessebrault.ssg
import com.jessebrault.ssg.buildscript.DefaultBuildScriptConfiguratorFactory import com.jessebrault.ssg.buildscript.BuildScriptConfiguratorFactory
import com.jessebrault.ssg.util.Diagnostic import com.jessebrault.ssg.util.Diagnostic
import groovy.transform.PackageScope import groovy.transform.PackageScope
@ -9,40 +9,43 @@ import java.util.function.Consumer
@PackageScope @PackageScope
final class CliBasedStaticSiteGenerator implements StaticSiteGenerator { final class CliBasedStaticSiteGenerator implements StaticSiteGenerator {
private final File baseDir
private final File buildScript private final File buildScript
private final File tmpDir private final Collection<URL> buildScriptClassLoaderUrls
private final Map<String, String> scriptArgs
private final GroovyScriptEngine engine
private StaticSiteGenerator staticSiteGenerator private BuildScriptBasedStaticSiteGenerator delegate
/**
* @param buildScript The buildScript File.
* @param buildScriptClassLoaderUrls all the necessary urls to needed to run the given buildScript.
* Likely includes the parent directory of the buildScript, as well as buildSrc dir(s).
*/
CliBasedStaticSiteGenerator( CliBasedStaticSiteGenerator(
File baseDir,
File buildScript, File buildScript,
File tmpDir, Collection<URL> buildScriptClassLoaderUrls
GroovyScriptEngine engine,
Map<String, String> scriptArgs
) { ) {
this.baseDir = baseDir
this.buildScript = buildScript this.buildScript = buildScript
this.tmpDir = tmpDir this.buildScriptClassLoaderUrls = buildScriptClassLoaderUrls
this.scriptArgs = scriptArgs
this.engine = engine
} }
@Override @Override
boolean doBuild(String buildName, Consumer<Collection<Diagnostic>> diagnosticsConsumer) { boolean doBuild(
if (this.staticSiteGenerator == null) { String buildName,
this.staticSiteGenerator = new BuildScriptBasedStaticSiteGenerator( Collection<BuildScriptConfiguratorFactory> configuratorFactories,
this.engine, Map<String, Object> buildScriptArgs,
[new DefaultBuildScriptConfiguratorFactory(this.baseDir, this.tmpDir, this.engine)], Consumer<Collection<Diagnostic>> diagnosticsConsumer
this.buildScript, ) {
this.scriptArgs if (this.delegate == null) {
this.delegate = new BuildScriptBasedStaticSiteGenerator(
this.buildScriptClassLoaderUrls,
this.buildScript
) )
} }
this.staticSiteGenerator.doBuild(buildName, diagnosticsConsumer) this.delegate.doBuild(buildName, configuratorFactories, buildScriptArgs, diagnosticsConsumer)
}
GroovyClassLoader getBuildScriptClassLoader() {
this.delegate.buildScriptClassLoader
} }
} }

View File

@ -1,5 +1,6 @@
package com.jessebrault.ssg package com.jessebrault.ssg
import com.jessebrault.ssg.buildscript.DefaultBuildScriptConfiguratorFactory
import org.apache.logging.log4j.LogManager import org.apache.logging.log4j.LogManager
import org.apache.logging.log4j.Logger import org.apache.logging.log4j.Logger
import picocli.CommandLine import picocli.CommandLine
@ -17,13 +18,15 @@ final class SsgBuild extends AbstractBuildCommand {
protected Integer doSubCommand() { protected Integer doSubCommand() {
logger.traceEntry() logger.traceEntry()
def result = 0 def result = 0
def tmpDir = File.createTempDir()
def urls = [tmpDir, *this.buildSrcDirs, new File('.')].collect {
it.toURI().toURL()
} as URL[]
def engine = new GroovyScriptEngine(urls)
this.requestedBuilds.each { this.requestedBuilds.each {
def buildResult = this.doSingleBuild(it, tmpDir, engine) def buildResult = this.doSingleBuild(
it,
[new DefaultBuildScriptConfiguratorFactory(
new File('.'),
this.staticSiteGenerator::getBuildScriptClassLoader
)],
this.scriptArgs
)
if (buildResult == 1) { if (buildResult == 1) {
result = 1 result = 1
} }

View File

@ -1,5 +1,6 @@
package com.jessebrault.ssg package com.jessebrault.ssg
import com.jessebrault.ssg.buildscript.DefaultBuildScriptConfiguratorFactory
import com.jessebrault.ssg.util.Diagnostic import com.jessebrault.ssg.util.Diagnostic
import com.jessebrault.ssg.util.ResourceUtil import com.jessebrault.ssg.util.ResourceUtil
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
@ -14,20 +15,17 @@ final class CliBasedStaticSiteGeneratorTests {
def baseDir = File.createTempDir() def baseDir = File.createTempDir()
SsgInit.init(baseDir, true) SsgInit.init(baseDir, true)
def tmpDir = File.createTempDir() def groovyClassLoader = new GroovyClassLoader()
def engine = new GroovyScriptEngine([ [baseDir.toURI().toURL(), new File(baseDir, 'buildSrc').toURI().toURL()]
new File(baseDir, 'buildSrc').toURI().toURL(), .each(groovyClassLoader::addURL)
tmpDir.toURI().toURL()
] as URL[]) def ssg = new CliBasedStaticSiteGenerator(new File(baseDir, 'ssgBuilds.groovy'), [])
def ssg = new CliBasedStaticSiteGenerator( def configuratorFactories = [
baseDir, new DefaultBuildScriptConfiguratorFactory(baseDir, ssg::getBuildScriptClassLoader)
new File(baseDir, 'ssgBuilds.groovy'), ]
tmpDir,
engine,
[:]
)
def diagnostics = [] as Collection<Diagnostic> def diagnostics = [] as Collection<Diagnostic>
assertTrue(ssg.doBuild('production', diagnostics.&addAll), { assertTrue(ssg.doBuild('production', configuratorFactories, [:], diagnostics.&addAll), {
diagnostics.inject('') { acc, diagnostic -> diagnostics.inject('') { acc, diagnostic ->
acc + '\n' + diagnostic.message acc + '\n' + diagnostic.message
} }