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 {
// 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
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.BuildScriptConfiguratorFactory
import com.jessebrault.ssg.buildscript.BuildScripts
import com.jessebrault.ssg.buildscript.BuildScriptRunner
import com.jessebrault.ssg.util.Diagnostic
import org.jetbrains.annotations.Nullable
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 exit = MarkerFactory.getMarker('exit')
private final GroovyScriptEngine engine
private final Collection<BuildScriptConfiguratorFactory> configuratorFactories
private final @Nullable File buildScript
private final Map<String, Object> scriptArgs
private final Collection<Build> builds = []
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(
GroovyScriptEngine engine,
Collection<BuildScriptConfiguratorFactory> configuratorFactories = [],
@Nullable File buildScript = null,
Map<String, Object> scriptArgs = [:]
Collection<URL> buildScriptClassLoaderUrls,
@Nullable File buildScript = null
) {
this.engine = engine
this.configuratorFactories = configuratorFactories
this.buildScript = buildScript
this.scriptArgs = scriptArgs
}
private void runBuildScript() {
logger.trace(enter, '')
private void runBuildScript(
Collection<BuildScriptConfiguratorFactory> configuratorFactories,
Map<String, Object> buildScriptArgs
) {
logger.trace(enter, 'configuratorFactories: {}, buildScriptArgs: {}', configuratorFactories, buildScriptArgs)
if (this.buildScript == null) {
logger.info('no specified build script; using defaults')
def result = BuildScripts.runBuildScript { base ->
this.configuratorFactories.each {
def result = BuildScriptRunner.runClosureScript { base ->
configuratorFactories.each {
it.get().accept(base)
}
}
this.builds.addAll(result)
} else if (this.buildScript.exists() && this.buildScript.isFile()) {
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.engine,
[args: this.scriptArgs]
[args: buildScriptArgs]
) { base ->
this.configuratorFactories.each { it.get().accept(base) }
configuratorFactories.each { it.get().accept(base) }
}
this.builds.addAll(result)
} else {
@ -67,12 +72,27 @@ final class BuildScriptBasedStaticSiteGenerator implements StaticSiteGenerator {
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
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)
if (!this.ranBuildScript) {
this.runBuildScript()
this.runBuildScript(configuratorFactories, buildScriptArgs)
}
def build = this.builds.find { it.name == buildName }

View File

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

View File

@ -13,8 +13,7 @@ import groovy.transform.EqualsAndHashCode
import groovy.transform.NullCheck
import groovy.transform.TupleConstructor
@TupleConstructor(defaults = false)
@NullCheck(includeGenerated = true)
@NullCheck
@EqualsAndHashCode
final class SourceProviders {
@ -26,22 +25,24 @@ final class SourceProviders {
sp0.modelsProvider + sp1.modelsProvider,
sp0.pagesProvider + sp1.pagesProvider,
sp0.templatesProvider + sp1.templatesProvider,
sp0.partsProvider + sp1.partsProvider
sp0.partsProvider + sp1.partsProvider,
sp0.custom + sp1.custom
)
}
static SourceProviders get(Map<String, Object> args) {
new SourceProviders(
args?.textsProvider as CollectionProvider<Text>
args.textsProvider 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>>,
args?.pagesProvider as CollectionProvider<Page>
args.pagesProvider as CollectionProvider<Page>
?: CollectionProviders.getEmpty() as CollectionProvider<Page>,
args?.templatesProvider as CollectionProvider<Template>
args.templatesProvider as CollectionProvider<Template>
?: CollectionProviders.getEmpty() as CollectionProvider<Template>,
args?.partsProvider as CollectionProvider<Part>
?: CollectionProviders.getEmpty() as CollectionProvider<Part>
args.partsProvider 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(),
[:]
)
}
@ -61,10 +63,40 @@ final class SourceProviders {
final CollectionProvider<Template> templatesProvider
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) {
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
String toString() {
"SourceProviders(textsProvider: ${ this.textsProvider }, modelsProvider: ${ this.modelsProvider }, " +

View File

@ -21,6 +21,8 @@ final class SourceProvidersDelegate {
private CollectionProvider<Template> templatesProvider = CollectionProviders.getEmpty()
private CollectionProvider<Part> partsProvider = CollectionProviders.getEmpty()
private final Map<String, CollectionProvider<Object>> custom = [:]
void texts(CollectionProvider<Text> textsProvider) {
this.textsProvider += textsProvider
}
@ -41,13 +43,18 @@ final class SourceProvidersDelegate {
this.partsProvider += partsProvider
}
void custom(String name, CollectionProvider<?> customProvider) {
this.custom.put(name, customProvider as CollectionProvider<Object>)
}
SourceProviders getResult() {
new SourceProviders(
this.textsProvider,
this.modelsProvider,
this.pagesProvider,
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
import com.jessebrault.ssg.page.Page
import com.jessebrault.ssg.provider.CollectionProvider
import com.jessebrault.ssg.provider.CollectionProviders
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.util.Result
import static com.jessebrault.ssg.util.ExtensionUtil.stripExtension
import static java.util.Objects.requireNonNull
final class PageToHtmlTaskFactory extends AbstractRenderTaskFactory {
CollectionProvider<Page> pagesProvider = CollectionProviders.getEmpty()
CollectionProvider<PageToHtmlSpec> specsProvider = CollectionProviders.getEmpty()
@Override
Result<Collection<Task>> getTasks(TaskSpec taskSpec) {
super.checkProviders()
requireNonNull(this.pagesProvider)
this.checkProviders()
requireNonNull(this.specsProvider)
def allTexts = this.allTextsProvider.provide()
def allModels = this.allModelsProvider.provide()
def allParts = this.allPartsProvider.provide()
final Collection<Task> tasks = this.pagesProvider.provide()
final Collection<Task> tasks = this.specsProvider.provide()
.collect {
new PageToHtmlTask(
stripExtension(it.path) + '.html',
it.toRelativeHtmlPath.apply(taskSpec),
taskSpec,
it,
it.page,
allTexts,
allModels,
allParts

View File

@ -1,16 +1,19 @@
package com.jessebrault.ssg.html
import com.jessebrault.ssg.task.TaskSpec
import com.jessebrault.ssg.template.Template
import com.jessebrault.ssg.text.Text
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 TextToHtmlSpec {
final Text text
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.provider.CollectionProvider
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.Result
import java.util.function.Function
final class TextToHtmlSpecProviders {
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 {
def templates = sources.templatesProvider.provide()
sources.textsProvider.provide().findResults {
def templates = templatesProvider.provide()
textsProvider.provide().findResults {
def frontMatterResult = it.type.frontMatterGetter.get(it)
if (frontMatterResult.hasDiagnostics()) {
return Result.ofDiagnostics(frontMatterResult.diagnostics) as Result<TextToHtmlSpec>
@ -22,7 +46,7 @@ final class TextToHtmlSpecProviders {
return Result.of(new TextToHtmlSpec(
it,
template,
ExtensionUtil.stripExtension(it.path) + '.html'
toRelativeHtmlPath.apply(it)
))
} else {
return null

View File

@ -12,12 +12,12 @@ import static java.util.Objects.requireNonNull
final class TextToHtmlTaskFactory extends AbstractRenderTaskFactory {
CollectionProvider<Result<TextToHtmlSpec>> specProvider = CollectionProviders.getEmpty()
CollectionProvider<Result<TextToHtmlSpec>> specsProvider = CollectionProviders.getEmpty()
@Override
Result<Collection<Task>> getTasks(TaskSpec taskSpec) {
super.checkProviders()
requireNonNull(this.specProvider)
requireNonNull(this.specsProvider)
def allTexts = this.allTextsProvider.provide()
def allModels = this.allModelsProvider.provide()
@ -25,23 +25,22 @@ final class TextToHtmlTaskFactory extends AbstractRenderTaskFactory {
Collection<Diagnostic> diagnostics = []
final Collection<Task> tasks = this.specProvider.provide()
.findResults {
if (it.hasDiagnostics()) {
diagnostics.addAll(it.diagnostics)
} else {
def spec = it.get()
new TextToHtmlTask(
spec.path,
taskSpec,
spec.text,
spec.template,
allTexts,
allModels,
allParts
)
}
}
final Collection<Task> tasks = this.specsProvider.provide().findResults {
if (it.hasDiagnostics()) {
diagnostics.addAll(it.diagnostics)
} else {
def spec = it.get()
new TextToHtmlTask(
spec.toRelativeHtmlPath.apply(taskSpec),
taskSpec,
spec.text,
spec.template,
allTexts,
allModels,
allParts
)
}
}
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.StandardGspRenderer
import com.jessebrault.ssg.util.Diagnostic
import com.jessebrault.ssg.util.Result
import groovy.transform.EqualsAndHashCode
import groovy.transform.NullCheck
@ -13,8 +12,8 @@ final class GspPageRenderer implements PageRenderer {
private final StandardGspRenderer gspRenderer
GspPageRenderer(File tmpDir, GroovyScriptEngine engine) {
this.gspRenderer = new StandardGspRenderer(tmpDir, engine)
GspPageRenderer(ClassLoader parentClassLoader) {
this.gspRenderer = new StandardGspRenderer(parentClassLoader)
}
@Override

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,14 +1,10 @@
package com.jessebrault.ssg
import com.jessebrault.ssg.buildscript.BuildScriptBase
import com.jessebrault.ssg.buildscript.BuildScriptConfiguratorFactory
import com.jessebrault.ssg.util.ResourceUtil
import org.junit.jupiter.api.Test
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import java.util.function.Consumer
import static com.jessebrault.ssg.util.FileAssertions.assertFileStructureAndContents
import static org.junit.jupiter.api.Assertions.assertTrue
@ -32,19 +28,17 @@ final class BuildScriptBasedStaticSiteGeneratorTests {
}
}
def tmpDir = File.createTempDir()
def engine = new GroovyScriptEngine([sourceDir.toURI().toURL(), tmpDir.toURI().toURL()] as URL[])
def ssg = new BuildScriptBasedStaticSiteGenerator(engine, [new BuildScriptConfiguratorFactory() {
def ssg = new BuildScriptBasedStaticSiteGenerator([sourceDir.toURI().toURL()], buildScript)
@Override
Consumer<BuildScriptBase> get() {
return { }
}
}], buildScript, [sourceDir: sourceDir, tmpDir: tmpDir, engine: engine])
assertTrue(ssg.doBuild('test') {
it.each { logger.error(it.toString()) }
})
assertTrue(
ssg.doBuild(
'test',
[],
[sourceDir: sourceDir, gclSupplier: ssg::getBuildScriptClassLoader]
) {
it.each { logger.error(it.toString()) }
}
)
def expectedBase = File.createTempDir()
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.Function
import static com.jessebrault.ssg.buildscript.BuildScripts.runBuildScript
import static BuildScriptRunner.runClosureScript
import static org.junit.jupiter.api.Assertions.assertEquals
import static org.mockito.Mockito.verify
@NullCheck
@ExtendWith(MockitoExtension)
final class BuildScriptsTests {
@NullCheck
@ExtendWith(MockitoExtension)
static final class ScriptFileTests {
/**
* Must be non-static, otherwise Groovy gets confused inside the Closures.
*
* TODO: use the FileUtil.copyResourceToWriter method
*/
@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.
*
* TODO: use the FileUtil.copyResourceToWriter method
*/
@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 {
@Test
void simple() {
def result = runBuildScript {
build(name: 'test') { }
}
assertEquals(1, result.size())
assertEquals('test', result[0].name)
}
@Test
void oneBuildOutputDirWithFunction(@Mock Function<Build, OutputDir> mockOutputDirFunction) {
def r = runBuildScript {
build(name: 'test') {
outputDirFunction = mockOutputDirFunction
/**
* 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)
}
}
assertEquals(1, r.size())
def b0 = r[0]
assertEquals(mockOutputDirFunction, b0.outputDirFunction)
}
tempDir
}
@Test
void oneBuildOutputDirWithFile() {
def f = new File('test')
def r = runBuildScript {
build(name: 'test') {
outputDir = f
private static Collection<Build> getBuilds(
String scriptName,
Collection<URL> urls,
Map<String, Object> binding = [:],
Consumer<BuildScriptBase> configureBase = { }
) {
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
void oneBuildOutputDirWithString() {
def r = runBuildScript {
build(name: 'test') {
outputDir = 'test'
@Test
void withBinding(@Mock Consumer<String> stringConsumer) {
def baseDir = this.setupScripts(['withBinding.groovy'])
getBuilds('withBinding.groovy', [baseDir.toURI().toURL()], [stringConsumer: stringConsumer])
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
void oneBuildSiteSpec() {
def r = runBuildScript {
build(name: 'test') {
siteSpec {
name = 'testSite'
baseUrl = 'https://testsite.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'
}
@Test
void oneBuildWithAbstractParent() {
def r = runClosureScript {
abstractBuild(name: 'parent') {
siteSpec {
name = 'Test Site'
baseUrl = 'https://test.com'
}
}
assertEquals(1, r.size())
def b0 = r[0]
assertEquals(new SiteSpec('Test Site', 'https://test.com/child'), b0.siteSpec)
build(name: 'child', extending: 'parent') {
siteSpec { base ->
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 {
private static GspPageRenderer getRenderer(
ClassLoader classLoader,
Collection<URL> urls
ClassLoader parentClassLoader = GspPageRendererTests.classLoader
) {
def tmpDir = File.createTempDir()
def engine = new GroovyScriptEngine([tmpDir.toURI().toURL(), *urls] as URL[], classLoader)
new GspPageRenderer(tmpDir, engine)
new GspPageRenderer(parentClassLoader)
}
@Override
Result<String> render(String scriptlet, RenderContext context, ClassLoader classLoader, Collection<URL> urls) {
def renderer = getRenderer(classLoader, urls)
Result<String> render(String scriptlet, RenderContext context, ClassLoader classLoader) {
def renderer = getRenderer(classLoader)
renderer.render(
new Page('', new PageType([], renderer), scriptlet),
context

View File

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

View File

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

View File

@ -9,13 +9,14 @@ import com.jessebrault.ssg.text.TextTypes
import com.jessebrault.ssg.text.TextsProviders
import groovy.transform.BaseScript
import java.util.function.Supplier
@BaseScript
BuildScriptBase b
final class Args {
File sourceDir
File tmpDir
GroovyScriptEngine engine
Supplier<GroovyClassLoader> gclSupplier
}
def args = args as Args
@ -25,7 +26,7 @@ build(name: 'test') {
types {
textTypes << TextTypes.MARKDOWN
templateTypes << TemplateTypes.getGsp(['.gsp'], args.tmpDir, args.engine)
templateTypes << TemplateTypes.getGsp(['.gsp'], args.gclSupplier.get())
}
sources { base, types ->
@ -35,7 +36,7 @@ build(name: 'test') {
taskFactories { base, sources ->
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)
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) {
assertEmptyDiagnostics(result)
@ -44,21 +44,19 @@ interface StandardDslConsumerTests {
String expected,
String scriptlet,
RenderContext context = new RenderContext(),
ClassLoader classLoader = this.class.classLoader,
Collection<URL> urls = []
ClassLoader classLoader = this.class.classLoader
) {
this.checkResult(expected, this.render(scriptlet, context, classLoader, urls))
this.checkResult(expected, this.render(scriptlet, context, classLoader))
}
default void doDslAssertionTest(
String scriptlet,
RenderContext context = new RenderContext(),
ClassLoader classLoader = this.class.classLoader,
Collection<URL> urls = []
ClassLoader classLoader = this.class.classLoader
) {
Result<String> result = null
try {
result = this.render(scriptlet, context, classLoader, urls)
result = this.render(scriptlet, context, classLoader)
} catch (Throwable e) {
fail(e)
}
@ -279,12 +277,13 @@ interface StandardDslConsumerTests {
['com', 'jessebrault', 'ssg', 'tmp'],
greeterText
)
def configuredClassLoader = new GroovyClassLoader(this.class.classLoader)
configuredClassLoader.addURL(greeterBaseUrl)
this.doDslRenderTest(
"Hello, World!",
"<%@ import com.jessebrault.ssg.tmp.TmpGreeter %><%= new TmpGreeter().greet() %>",
new RenderContext(),
parentClassLoader,
[greeterBaseUrl]
configuredClassLoader
)
}

View File

@ -1,5 +1,6 @@
package com.jessebrault.ssg
import com.jessebrault.ssg.buildscript.BuildScriptConfiguratorFactory
import com.jessebrault.ssg.util.Diagnostic
import org.apache.logging.log4j.LogManager
import org.apache.logging.log4j.Logger
@ -37,23 +38,32 @@ abstract class AbstractBuildCommand extends AbstractSubCommand {
)
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)
if (this.staticSiteGenerator == null) {
this.staticSiteGenerator = new CliBasedStaticSiteGenerator(
new File('.'),
this.buildScript,
tmpDir,
engine,
this.scriptArgs
[
this.buildScript.parentFile.toURI().toURL(),
*this.buildSrcDirs.collect { it.toURI().toURL() }
]
)
}
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) }
logger.traceExit(1)
} else {

View File

@ -1,6 +1,6 @@
package com.jessebrault.ssg
import com.jessebrault.ssg.buildscript.DefaultBuildScriptConfiguratorFactory
import com.jessebrault.ssg.buildscript.BuildScriptConfiguratorFactory
import com.jessebrault.ssg.util.Diagnostic
import groovy.transform.PackageScope
@ -9,40 +9,43 @@ import java.util.function.Consumer
@PackageScope
final class CliBasedStaticSiteGenerator implements StaticSiteGenerator {
private final File baseDir
private final File buildScript
private final File tmpDir
private final Map<String, String> scriptArgs
private final GroovyScriptEngine engine
private final Collection<URL> buildScriptClassLoaderUrls
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(
File baseDir,
File buildScript,
File tmpDir,
GroovyScriptEngine engine,
Map<String, String> scriptArgs
Collection<URL> buildScriptClassLoaderUrls
) {
this.baseDir = baseDir
this.buildScript = buildScript
this.tmpDir = tmpDir
this.scriptArgs = scriptArgs
this.engine = engine
this.buildScriptClassLoaderUrls = buildScriptClassLoaderUrls
}
@Override
boolean doBuild(String buildName, Consumer<Collection<Diagnostic>> diagnosticsConsumer) {
if (this.staticSiteGenerator == null) {
this.staticSiteGenerator = new BuildScriptBasedStaticSiteGenerator(
this.engine,
[new DefaultBuildScriptConfiguratorFactory(this.baseDir, this.tmpDir, this.engine)],
this.buildScript,
this.scriptArgs
boolean doBuild(
String buildName,
Collection<BuildScriptConfiguratorFactory> configuratorFactories,
Map<String, Object> buildScriptArgs,
Consumer<Collection<Diagnostic>> diagnosticsConsumer
) {
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
import com.jessebrault.ssg.buildscript.DefaultBuildScriptConfiguratorFactory
import org.apache.logging.log4j.LogManager
import org.apache.logging.log4j.Logger
import picocli.CommandLine
@ -17,13 +18,15 @@ final class SsgBuild extends AbstractBuildCommand {
protected Integer doSubCommand() {
logger.traceEntry()
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 {
def buildResult = this.doSingleBuild(it, tmpDir, engine)
def buildResult = this.doSingleBuild(
it,
[new DefaultBuildScriptConfiguratorFactory(
new File('.'),
this.staticSiteGenerator::getBuildScriptClassLoader
)],
this.scriptArgs
)
if (buildResult == 1) {
result = 1
}

View File

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