diff --git a/.run/debug-test-project.run.xml b/.run/debug-test-project.run.xml
new file mode 100644
index 0000000..b7ef759
--- /dev/null
+++ b/.run/debug-test-project.run.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.run/launch-debug-test-project.run.xml b/.run/launch-debug-test-project.run.xml
new file mode 100644
index 0000000..27124ba
--- /dev/null
+++ b/.run/launch-debug-test-project.run.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.run/run-test-project.run.xml b/.run/run-test-project.run.xml
new file mode 100644
index 0000000..890c4e0
--- /dev/null
+++ b/.run/run-test-project.run.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/api/build.gradle b/api/build.gradle
index 8a11427..ea99501 100644
--- a/api/build.gradle
+++ b/api/build.gradle
@@ -3,6 +3,7 @@ plugins {
id 'groovy'
id 'java-library'
id 'java-test-fixtures'
+ id 'maven-publish'
}
repositories {
@@ -18,26 +19,26 @@ configurations {
dependencies {
api libs.groovy
+ api libs.groovy.yaml
api libs.groowt.all
compileOnlyApi libs.jetbrains.anontations
- implementation libs.groovy.templates
+ implementation libs.classgraph
implementation libs.commonmark
implementation libs.commonmark.frontmatter
implementation libs.jsoup
-
- // https://archiva.jessebrault.com/#artifact/com.jessebrault.gst/lib
- implementation 'com.jessebrault.gst:lib:0.0.5'
-
- // https://mvnrepository.com/artifact/org.jgrapht/jgrapht-core
- implementation 'org.jgrapht:jgrapht-core:1.5.2'
-
- // So we can use Grape dependency management
- // https://mvnrepository.com/artifact/org.apache.ivy/ivy
- runtimeOnly 'org.apache.ivy:ivy:2.5.2'
}
jar {
archivesBaseName = 'ssg-api'
}
+
+publishing {
+ publications {
+ create('ssgApi', MavenPublication) {
+ artifactId = 'ssg-api'
+ from components.java
+ }
+ }
+}
diff --git a/api/src/main/groovy/com/jessebrault/ssg/BuildScriptBasedStaticSiteGenerator.groovy b/api/src/main/groovy/com/jessebrault/ssg/BuildScriptBasedStaticSiteGenerator.groovy
deleted file mode 100644
index 2282efd..0000000
--- a/api/src/main/groovy/com/jessebrault/ssg/BuildScriptBasedStaticSiteGenerator.groovy
+++ /dev/null
@@ -1,158 +0,0 @@
-package com.jessebrault.ssg
-
-import com.jessebrault.ssg.buildscript.BuildSpec
-import com.jessebrault.ssg.buildscript.BuildScriptConfiguratorFactory
-import com.jessebrault.ssg.buildscript.FileBuildScriptGetter
-import com.jessebrault.ssg.util.Diagnostic
-import groovy.transform.EqualsAndHashCode
-import groovy.transform.NullCheck
-import groovy.transform.TupleConstructor
-import org.jetbrains.annotations.Nullable
-import org.slf4j.Logger
-import org.slf4j.LoggerFactory
-import org.slf4j.Marker
-import org.slf4j.MarkerFactory
-
-import java.util.function.Consumer
-
-final class BuildScriptBasedStaticSiteGenerator implements StaticSiteGenerator {
-
- private static final Logger logger = LoggerFactory.getLogger(BuildScriptBasedStaticSiteGenerator)
- private static final Marker enter = MarkerFactory.getMarker('enter')
- private static final Marker exit = MarkerFactory.getMarker('exit')
-
- private final Collection buildScriptClassLoaderUrls
- private final @Nullable File buildScript
- private final Collection builds = []
-
- 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 null
.
- */
- BuildScriptBasedStaticSiteGenerator(
- Collection buildScriptClassLoaderUrls,
- @Nullable File buildScript = null
- ) {
- this.buildScriptClassLoaderUrls = buildScriptClassLoaderUrls
- this.buildScript = buildScript
- }
-
- private void runBuildScript(
- Collection configuratorFactories,
- Map buildScriptArgs
- ) {
- logger.trace(enter, 'configuratorFactories: {}, buildScriptArgs: {}', configuratorFactories, buildScriptArgs)
-
- if (this.buildScript == null) {
- logger.info('no specified build script; using defaults')
- def result = FileBuildScriptGetter.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 buildScriptRunner = new FileBuildScriptGetter(this.buildScriptClassLoaderUrls)
- this.buildScriptClassLoader = buildScriptRunner.getBuildScriptClassLoader()
- def result = buildScriptRunner.getBuildScript(
- this.buildScript.name,
- [args: buildScriptArgs]
- ) { base ->
- configuratorFactories.each { it.get().accept(base) }
- }
- this.builds.addAll(result)
- } else {
- throw new IllegalArgumentException("given buildScript ${ this.buildScript } either does not exist or is not a file")
- }
-
- 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)
- }
-
- @TupleConstructor(defaults = false)
- @NullCheck(includeGenerated = true)
- @EqualsAndHashCode
- private static final class IncludedBuildsResult {
- final Collection builds
- final Collection diagnostics
- }
-
- // TODO: cache build script results
- @Override
- boolean doBuild(
- String buildName,
- Collection configuratorFactories,
- Map buildScriptArgs,
- Consumer> diagnosticsConsumer
- ) {
- logger.trace(enter, 'buildName: {}, diagnosticsConsumer: {}', buildName, diagnosticsConsumer)
-
- this.runBuildScript(configuratorFactories, buildScriptArgs)
-
- def build = this.builds.find { it.name == buildName }
- if (!build) {
- throw new IllegalArgumentException("there is no registered build with name: ${ buildName }")
- }
-
- def includedBuildsResult = build.includedBuilds.inject(
- new IncludedBuildsResult([], [])
- ) { acc, includedBuildName ->
- def includedBuild = this.builds.find { it.name == includedBuildName }
- if (includedBuild == null) {
- acc.diagnostics << new Diagnostic("There is no registered build ${ includedBuildName } that can be included.")
- } else {
- acc.builds << includedBuild
- }
- acc
- }
-
- if (includedBuildsResult.diagnostics.size() > 0) {
- diagnosticsConsumer.accept(includedBuildsResult.diagnostics)
- logger.trace(exit, 'result: false')
- return false
- }
-
- def buildTasksConverter = new SimpleBuildTasksConverter()
-
- def allBuilds = includedBuildsResult.builds + build
- def allBuildsConvertResults = allBuilds.collect {
- buildTasksConverter.convert(it)
- }
- def allBuildsConvertDiagnostics = allBuildsConvertResults.collectMany {
- it.diagnostics
- }
-
- if (allBuildsConvertDiagnostics.size() > 0) {
- diagnosticsConsumer.accept(allBuildsConvertDiagnostics)
- logger.trace(exit, 'result: false')
- return false
- }
-
- def allTasks = allBuildsConvertResults.collectMany {
- it.get()
- }
- def allTasksDiagnostics = allTasks.collectMany { it.execute(allTasks) }
- if (allTasksDiagnostics.size() > 0) {
- diagnosticsConsumer.accept(allTasksDiagnostics)
- logger.trace(exit, 'result: false')
- return false
- }
-
- logger.trace(exit, 'result: true')
- return true
- }
-
-}
diff --git a/api/src/main/groovy/com/jessebrault/ssg/BuildTasksConverter.groovy b/api/src/main/groovy/com/jessebrault/ssg/BuildTasksConverter.groovy
deleted file mode 100644
index fbbff9c..0000000
--- a/api/src/main/groovy/com/jessebrault/ssg/BuildTasksConverter.groovy
+++ /dev/null
@@ -1,9 +0,0 @@
-package com.jessebrault.ssg
-
-import com.jessebrault.ssg.buildscript.BuildSpec
-import com.jessebrault.ssg.task.Task
-import com.jessebrault.ssg.util.Result
-
-interface BuildTasksConverter {
- Result> convert(BuildSpec buildScriptResult)
-}
diff --git a/api/src/main/groovy/com/jessebrault/ssg/DefaultStaticSiteGenerator.groovy b/api/src/main/groovy/com/jessebrault/ssg/DefaultStaticSiteGenerator.groovy
new file mode 100644
index 0000000..337a6be
--- /dev/null
+++ b/api/src/main/groovy/com/jessebrault/ssg/DefaultStaticSiteGenerator.groovy
@@ -0,0 +1,228 @@
+package com.jessebrault.ssg
+
+import com.jessebrault.ssg.buildscript.BuildScriptGetter
+import com.jessebrault.ssg.buildscript.BuildScriptToBuildSpecConverter
+import com.jessebrault.ssg.buildscript.BuildSpec
+import com.jessebrault.ssg.buildscript.delegates.BuildDelegate
+import com.jessebrault.ssg.di.*
+import com.jessebrault.ssg.page.DefaultPage
+import com.jessebrault.ssg.page.Page
+import com.jessebrault.ssg.page.PageFactory
+import com.jessebrault.ssg.page.PageSpec
+import com.jessebrault.ssg.text.Text
+import com.jessebrault.ssg.util.Diagnostic
+import com.jessebrault.ssg.view.PageView
+import groovy.transform.TupleConstructor
+import groowt.util.di.RegistryObjectFactory
+import io.github.classgraph.ClassGraph
+import org.slf4j.Logger
+import org.slf4j.LoggerFactory
+
+import java.nio.file.Files
+
+import static groowt.util.di.BindingUtil.named
+import static groowt.util.di.BindingUtil.toSingleton
+
+@TupleConstructor(includeFields = true, defaults = false)
+class DefaultStaticSiteGenerator implements StaticSiteGenerator {
+
+ private static final Logger logger = LoggerFactory.getLogger(DefaultStaticSiteGenerator)
+
+ protected final GroovyClassLoader groovyClassLoader
+ protected final URL[] buildScriptBaseUrls
+ protected final boolean dryRun
+
+ protected Set getTexts(String buildScriptFqn, BuildSpec buildSpec) {
+ def textConverters = buildSpec.textConverters.get {
+ new SsgException("The textConverters Property in $buildScriptFqn must contain at least an empty Set.")
+ }
+ def textDirs = buildSpec.textsDirs.get {
+ new SsgException("The textDirs Property in $buildScriptFqn must contain at least an empty Set.")
+ }
+ def texts = [] as Set
+ textDirs.each { textDir ->
+ if (textDir.exists()) {
+ Files.walk(textDir.toPath()).each {
+ def asFile = it.toFile()
+ def lastDot = asFile.name.lastIndexOf('.')
+ if (lastDot != -1) {
+ def extension = asFile.name.substring(lastDot)
+ def converter = textConverters.find {
+ it.handledExtensions.contains(extension)
+ }
+ texts << converter.convert(textDir, asFile)
+ }
+ }
+ }
+ }
+ texts
+ }
+
+ @Override
+ Collection doBuild(
+ File projectDir,
+ String buildName,
+ String buildScriptFqn,
+ Map buildScriptCliArgs
+ ) {
+ // run build script(s) and get buildSpec
+ def buildScriptGetter = new BuildScriptGetter(
+ this.groovyClassLoader,
+ this.buildScriptBaseUrls,
+ buildScriptCliArgs,
+ projectDir
+ )
+ def buildScriptToBuildSpecConverter = new BuildScriptToBuildSpecConverter(
+ buildScriptGetter,
+ BuildDelegate.withDefaults(projectDir)
+ )
+ def buildSpec = buildScriptToBuildSpecConverter.convert(buildScriptFqn)
+
+ // prepare objectFactory
+ def objectFactoryBuilder = buildSpec.objectFactoryBuilder.get {
+ new SsgException(
+ "the objectFactoryBuilder Property in $buildScriptFqn " +
+ "must contain a RegistryObjectFactory.Builder instance."
+ )
+ }
+
+ // configure for Page instantiation
+ objectFactoryBuilder.configureRegistry {
+ // extensions
+ addExtension(new TextsExtension().tap {
+ allTexts.addAll(this.getTexts(buildScriptFqn, buildSpec))
+ })
+ addExtension(new ModelsExtension().tap {
+ allModels.addAll(buildSpec.models.get {
+ new SsgException("The models Property in $buildScriptFqn must contain at least an empty Set.")
+ })
+ })
+ addExtension(new GlobalsExtension().tap {
+ globals.putAll(buildSpec.globals.get {
+ new SsgException("The globals Property in $buildScriptFqn must contain at least an empty Map.")
+ })
+ })
+
+ // bindings
+ bind(named('buildName', String), toSingleton(buildSpec.name))
+ bind(named('siteName', String), toSingleton(buildSpec.siteName.get {
+ new SsgException("The siteName Property in $buildScriptFqn must be set.")
+ }))
+ bind(named('baseUrl', String), toSingleton(buildSpec.baseUrl.get {
+ new SsgException("The baseUrl Property in $buildScriptFqn must be set.")
+ }))
+ }
+
+ // get the objectFactory
+ def objectFactory = objectFactoryBuilder.build()
+ objectFactory.configureRegistry {
+ bind(RegistryObjectFactory, toSingleton(objectFactory))
+ }
+
+ // prepare for basePackages scan for Pages and PageFactories
+ def basePackages = buildSpec.basePackages.get {
+ new SsgException("The basePackages Property in $buildScriptFqn must contain at least an empty Set.")
+ }
+ def classgraph = new ClassGraph()
+ .enableAnnotationInfo()
+ .addClassLoader(groovyClassLoader)
+ basePackages.each { classgraph.acceptPackages(it) }
+
+
+ def pages = [] as Set
+
+ try (def scanResult = classgraph.scan()) {
+ // single pages
+ def pageViewInfoList = scanResult.getClassesImplementing(PageView)
+ pageViewInfoList.each { pageViewInfo ->
+ def pageSpecInfo = pageViewInfo.getAnnotationInfo(PageSpec)
+ if (pageSpecInfo != null) {
+ def pageSpec = (PageSpec) pageSpecInfo.loadClassAndInstantiate()
+ pages << new DefaultPage(
+ pageSpec.name(),
+ pageSpec.path(),
+ pageSpec.fileExtension(),
+ (Class extends PageView>) pageViewInfo.loadClass()
+ )
+ }
+ }
+
+ // page factories
+ def pageFactoryTypes = [] as Set>
+
+ def pageFactoryInfoList = scanResult.getClassesImplementing(PageFactory)
+ pageFactoryInfoList.each { pageFactoryInfo ->
+ pageFactoryTypes << (pageFactoryInfo.loadClass() as Class extends PageFactory>)
+ }
+
+ // instantiate page factory and create the pages
+ pageFactoryTypes.each { pageFactoryType ->
+ def pageFactory = objectFactory.createInstance(pageFactoryType)
+ pages.addAll(pageFactory.create())
+ }
+ }
+
+ // Configure for PageView instantiation
+ objectFactoryBuilder.configureRegistry {
+ // extensions
+ addExtension(new PagesExtension().tap {
+ allPages.addAll(pages)
+ })
+ addExtension(new SelfPageExtension())
+ }
+
+ def diagnostics = [] as Collection
+
+ pages.each {
+ // instantiate PageView
+ PageView pageView
+ try {
+ objectFactory.registry.getExtension(SelfPageExtension).currentPage = it
+ pageView = objectFactory.createInstance(it.viewType)
+ } catch (Exception exception) {
+ diagnostics << new Diagnostic(
+ "There was an exception while constructing $it.viewType.name for $it.name",
+ exception
+ )
+ return
+ }
+
+ // Render the page
+ def sw = new StringWriter()
+ try {
+ pageView.renderTo(sw)
+ } catch (Exception exception) {
+ diagnostics << new Diagnostic(
+ "There was an exception while rendering $it.name as $pageView.class.name",
+ exception
+ )
+ return
+ }
+
+ // Output the page if not dryRun
+ if (!this.dryRun) {
+ def outputDir = buildSpec.outputDir.get {
+ new SsgException("The outputDir Property in $buildScriptFqn must be set.")
+ }
+ outputDir.mkdirs()
+
+ def outputFile = new File(
+ outputDir,
+ it.path.replace('/', File.separator) + it.fileExtension
+ )
+ try {
+ outputFile.write(sw.toString())
+ } catch (Exception exception) {
+ diagnostics << new Diagnostic(
+ "There was an exception while writing $it.name to $outputFile",
+ exception
+ )
+ }
+ }
+ }
+
+ // return diagnostics
+ diagnostics
+ }
+
+}
diff --git a/api/src/main/groovy/com/jessebrault/ssg/SimpleBuildTasksConverter.groovy b/api/src/main/groovy/com/jessebrault/ssg/SimpleBuildTasksConverter.groovy
deleted file mode 100644
index aa2308e..0000000
--- a/api/src/main/groovy/com/jessebrault/ssg/SimpleBuildTasksConverter.groovy
+++ /dev/null
@@ -1,33 +0,0 @@
-package com.jessebrault.ssg
-
-import com.jessebrault.ssg.buildscript.BuildSpec
-import com.jessebrault.ssg.task.Task
-import com.jessebrault.ssg.task.TaskSpec
-import com.jessebrault.ssg.util.Diagnostic
-import com.jessebrault.ssg.util.Result
-
-final class SimpleBuildTasksConverter implements BuildTasksConverter {
-
- @Override
- Result> convert(BuildSpec build) {
- def taskSpec = new TaskSpec(
- build.name,
- build.outputDirFunction.apply(build).asFile(),
- build.siteSpec,
- build.globals
- )
- Collection tasks = []
- Collection diagnostics = []
-
- build.taskFactorySpecs.each {
- def factory = it.supplier.get()
- it.configurators.each { it.accept(factory) }
- def result = factory.getTasks(taskSpec)
- diagnostics.addAll(result.diagnostics)
- tasks.addAll(result.get())
- }
-
- Result.of(diagnostics, tasks)
- }
-
-}
diff --git a/api/src/main/groovy/com/jessebrault/ssg/SiteSpec.groovy b/api/src/main/groovy/com/jessebrault/ssg/SiteSpec.groovy
deleted file mode 100644
index 04c068d..0000000
--- a/api/src/main/groovy/com/jessebrault/ssg/SiteSpec.groovy
+++ /dev/null
@@ -1,35 +0,0 @@
-package com.jessebrault.ssg
-
-import com.jessebrault.ssg.util.Monoid
-import com.jessebrault.ssg.util.Monoids
-import groovy.transform.EqualsAndHashCode
-import groovy.transform.NullCheck
-import groovy.transform.TupleConstructor
-
-@TupleConstructor(defaults = false)
-@NullCheck(includeGenerated = true)
-@EqualsAndHashCode
-final class SiteSpec {
-
- static final Monoid DEFAULT_MONOID = Monoids.of(getBlank(), SiteSpec::concat)
-
- static SiteSpec getBlank() {
- new SiteSpec('', '')
- }
-
- static SiteSpec concat(SiteSpec s0, SiteSpec s1) {
- new SiteSpec(
- s1.name.blank ? s0.name : s1.name,
- s1.baseUrl.blank ? s0.baseUrl : s1.baseUrl
- )
- }
-
- final String name
- final String baseUrl
-
- @Override
- String toString() {
- "SiteSpec(${ this.name }, ${ this.baseUrl })"
- }
-
-}
diff --git a/api/src/main/groovy/com/jessebrault/ssg/SsgException.groovy b/api/src/main/groovy/com/jessebrault/ssg/SsgException.groovy
new file mode 100644
index 0000000..1bad588
--- /dev/null
+++ b/api/src/main/groovy/com/jessebrault/ssg/SsgException.groovy
@@ -0,0 +1,13 @@
+package com.jessebrault.ssg
+
+class SsgException extends RuntimeException {
+
+ SsgException(String message) {
+ super(message)
+ }
+
+ SsgException(String message, Throwable cause) {
+ super(message, cause)
+ }
+
+}
diff --git a/api/src/main/groovy/com/jessebrault/ssg/StaticSiteGenerator.groovy b/api/src/main/groovy/com/jessebrault/ssg/StaticSiteGenerator.groovy
index f9b0e70..fa6729e 100644
--- a/api/src/main/groovy/com/jessebrault/ssg/StaticSiteGenerator.groovy
+++ b/api/src/main/groovy/com/jessebrault/ssg/StaticSiteGenerator.groovy
@@ -1,15 +1,12 @@
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(
+ Collection doBuild(
+ File projectDir,
String buildName,
- Collection configuratorFactories,
- Map buildScriptArgs,
- Consumer> diagnosticsConsumer
+ String buildScriptFqn,
+ Map buildScriptCliArgs
)
-}
\ No newline at end of file
+}
diff --git a/api/src/main/groovy/com/jessebrault/ssg/build/Build.groovy b/api/src/main/groovy/com/jessebrault/ssg/build/Build.groovy
deleted file mode 100644
index 0cc64d9..0000000
--- a/api/src/main/groovy/com/jessebrault/ssg/build/Build.groovy
+++ /dev/null
@@ -1,38 +0,0 @@
-package com.jessebrault.ssg.build
-
-import com.jessebrault.ssg.model.Model
-import com.jessebrault.ssg.page.Page
-import groowt.util.di.RegistryObjectFactory
-import groowt.util.fp.provider.NamedSetProvider
-
-import static com.jessebrault.ssg.util.ObjectUtil.*
-
-class Build {
-
- final String name
- final String siteName
- final String baseUrl
- final File outputDir
- final Map globals
- final Set textsDirs
- final NamedSetProvider pages
- final RegistryObjectFactory objectFactory
-
- Build(Map args) {
- this.name = requireString(args.name)
- this.siteName = requireString(args.siteName)
- this.baseUrl = requireString(args.baseUrl)
- this.outputDir = requireFile(args.outputDir)
- this.globals = requireMap(args.globals)
- this.textsDirs = requireSet(args.textsDirs)
- this.pages = requireType(NamedSetProvider, args.pages)
- this.objectFactory = requireType(RegistryObjectFactory, args.objectFactory)
- }
-
- void doBuild() {
- // set up object factory for di
- // container should have: Build and all its properties
- // container should also have @Text, @Texts, @Page, and @Pages resolvers
- }
-
-}
diff --git a/api/src/main/groovy/com/jessebrault/ssg/buildscript/BuildScriptBase.groovy b/api/src/main/groovy/com/jessebrault/ssg/buildscript/BuildScriptBase.groovy
index e76e1e1..b56cc0f 100644
--- a/api/src/main/groovy/com/jessebrault/ssg/buildscript/BuildScriptBase.groovy
+++ b/api/src/main/groovy/com/jessebrault/ssg/buildscript/BuildScriptBase.groovy
@@ -13,26 +13,26 @@ import static java.util.Objects.requireNonNull
@SuppressWarnings('unused')
abstract class BuildScriptBase extends Script {
+ /* --- Logging --- */
+
static final Logger logger = LoggerFactory.getLogger(BuildScriptBase)
static final Marker enter = MarkerFactory.getMarker('ENTER')
static final Marker exit = MarkerFactory.getMarker('EXIT')
+ /* --- build script proper --- */
+
private String extending
- private Closure buildClosure
+ private Closure buildClosure = { }
private File projectRoot
- File getProjectRoot() {
- requireNonNull(this.projectRoot)
- }
-
- void setProjectRoot(File projectRoot) {
- this.projectRoot = requireNonNull(projectRoot)
- }
+ /* --- Instance DSL helpers --- */
File file(String name) {
new File(this.projectRoot, name)
}
+ /* --- DSL --- */
+
void build(@Nullable String extending, @DelegatesTo(value = BuildDelegate) Closure buildClosure) {
this.extending = extending
this.buildClosure = buildClosure
@@ -43,8 +43,21 @@ abstract class BuildScriptBase extends Script {
this.buildClosure = buildClosure
}
+ /* --- internal --- */
+
@ApiStatus.Internal
- @Nullable String getExtending() {
+ File getProjectRoot() {
+ requireNonNull(this.projectRoot)
+ }
+
+ @ApiStatus.Internal
+ void setProjectRoot(File projectRoot) {
+ this.projectRoot = requireNonNull(projectRoot)
+ }
+
+ @ApiStatus.Internal
+ @Nullable
+ String getExtending() {
this.extending
}
diff --git a/api/src/main/groovy/com/jessebrault/ssg/buildscript/BuildScriptGetter.groovy b/api/src/main/groovy/com/jessebrault/ssg/buildscript/BuildScriptGetter.groovy
new file mode 100644
index 0000000..f6f4b26
--- /dev/null
+++ b/api/src/main/groovy/com/jessebrault/ssg/buildscript/BuildScriptGetter.groovy
@@ -0,0 +1,29 @@
+package com.jessebrault.ssg.buildscript
+
+import groovy.transform.NullCheck
+import groovy.transform.TupleConstructor
+import org.codehaus.groovy.control.CompilerConfiguration
+
+@NullCheck(includeGenerated = true)
+@TupleConstructor(includeFields = true, defaults = false)
+final class BuildScriptGetter {
+
+ private final GroovyClassLoader groovyClassLoader
+ private final URL[] scriptBaseUrls
+ private final Map scriptCliArgs
+ private final File projectDir
+
+ BuildScriptBase getAndRunBuildScript(String fqn) {
+ def gcl = new GroovyClassLoader(this.groovyClassLoader, new CompilerConfiguration().tap {
+ it.scriptBaseClass = BuildScriptBase.name
+ })
+ this.scriptBaseUrls.each { gcl.addURL(it) }
+ def scriptClass = gcl.loadClass(fqn, true, true) as Class
+ def buildScript = scriptClass.getConstructor().newInstance()
+ buildScript.binding = new Binding(this.scriptCliArgs)
+ buildScript.projectRoot = projectDir
+ buildScript.run()
+ buildScript
+ }
+
+}
diff --git a/api/src/main/groovy/com/jessebrault/ssg/buildscript/BuildDelegateToBuildSpecConverter.groovy b/api/src/main/groovy/com/jessebrault/ssg/buildscript/BuildScriptToBuildSpecConverter.groovy
similarity index 67%
rename from api/src/main/groovy/com/jessebrault/ssg/buildscript/BuildDelegateToBuildSpecConverter.groovy
rename to api/src/main/groovy/com/jessebrault/ssg/buildscript/BuildScriptToBuildSpecConverter.groovy
index 7164e6d..53acaad 100644
--- a/api/src/main/groovy/com/jessebrault/ssg/buildscript/BuildDelegateToBuildSpecConverter.groovy
+++ b/api/src/main/groovy/com/jessebrault/ssg/buildscript/BuildScriptToBuildSpecConverter.groovy
@@ -8,29 +8,32 @@ import java.util.function.Supplier
@NullCheck
@TupleConstructor(includeFields = true)
-class BuildDelegateToBuildSpecConverter {
+class BuildScriptToBuildSpecConverter {
- private final FileBuildScriptGetter buildScriptGetter
+ private final BuildScriptGetter buildScriptGetter
private final Supplier buildDelegateSupplier
protected BuildSpec getFromDelegate(String name, BuildDelegate delegate) {
new BuildSpec(
name: name,
+ basePackages: delegate.basePackages,
siteName: delegate.siteName,
baseUrl: delegate.baseUrl,
outputDir: delegate.outputDir,
globals: delegate.globals,
+ models: delegate.models,
textsDirs: delegate.textsDirs,
- models: delegate.models
+ textConverters: delegate.textConverters,
+ objectFactoryBuilder: delegate.objectFactoryBuilder
)
}
- BuildSpec convert(final String name, final BuildScriptBase buildScript) {
+ protected BuildSpec doConvert(String name, BuildScriptBase buildScript) {
final Deque buildHierarchy = new LinkedList<>()
buildHierarchy.push(buildScript)
String extending = buildScript.extending
while (extending != null) {
- def from = this.buildScriptGetter.getBuildScript(extending)
+ def from = this.buildScriptGetter.getAndRunBuildScript(extending)
buildHierarchy.push(from)
extending = from.extending
}
@@ -45,4 +48,9 @@ class BuildDelegateToBuildSpecConverter {
this.getFromDelegate(name, delegate)
}
+ BuildSpec convert(String buildScriptFqn) {
+ def start = this.buildScriptGetter.getAndRunBuildScript(buildScriptFqn)
+ this.doConvert(buildScriptFqn, start)
+ }
+
}
diff --git a/api/src/main/groovy/com/jessebrault/ssg/buildscript/BuildSpec.groovy b/api/src/main/groovy/com/jessebrault/ssg/buildscript/BuildSpec.groovy
index 7380cec..1d93ee0 100644
--- a/api/src/main/groovy/com/jessebrault/ssg/buildscript/BuildSpec.groovy
+++ b/api/src/main/groovy/com/jessebrault/ssg/buildscript/BuildSpec.groovy
@@ -1,37 +1,48 @@
package com.jessebrault.ssg.buildscript
import com.jessebrault.ssg.model.Model
+import com.jessebrault.ssg.text.TextConverter
import groovy.transform.EqualsAndHashCode
import groovy.transform.NullCheck
-import groowt.util.fp.provider.NamedProvider
+import groowt.util.di.RegistryObjectFactory
import groowt.util.fp.provider.Provider
-import static com.jessebrault.ssg.util.ObjectUtil.*
+import static com.jessebrault.ssg.util.ObjectUtil.requireProvider
+import static com.jessebrault.ssg.util.ObjectUtil.requireString
@NullCheck(includeGenerated = true)
@EqualsAndHashCode
final class BuildSpec {
final String name
+ final Provider> basePackages
final Provider siteName
final Provider baseUrl
final Provider outputDir
final Provider