Massive clean up and api/cli/gradle work. Running successfully now in test-ssg-project.
This commit is contained in:
parent
140dffefc6
commit
bc1d545297
16
.run/debug-test-project.run.xml
Normal file
16
.run/debug-test-project.run.xml
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<component name="ProjectRunConfigurationManager">
|
||||||
|
<configuration default="false" name="debug-test-project" type="Remote">
|
||||||
|
<module name="ssg" />
|
||||||
|
<option name="USE_SOCKET_TRANSPORT" value="true" />
|
||||||
|
<option name="SERVER_MODE" value="false" />
|
||||||
|
<option name="SHMEM_ADDRESS" />
|
||||||
|
<option name="HOST" value="localhost" />
|
||||||
|
<option name="PORT" value="8192" />
|
||||||
|
<option name="AUTO_RESTART" value="false" />
|
||||||
|
<RunnerSettings RunnerId="Debug">
|
||||||
|
<option name="DEBUG_PORT" value="8192" />
|
||||||
|
<option name="LOCAL" value="false" />
|
||||||
|
</RunnerSettings>
|
||||||
|
<method v="2" />
|
||||||
|
</configuration>
|
||||||
|
</component>
|
19
.run/launch-debug-test-project.run.xml
Normal file
19
.run/launch-debug-test-project.run.xml
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
<component name="ProjectRunConfigurationManager">
|
||||||
|
<configuration default="false" name="launch-debug-test-project" type="ShConfigurationType">
|
||||||
|
<option name="SCRIPT_TEXT" value="" />
|
||||||
|
<option name="INDEPENDENT_SCRIPT_PATH" value="true" />
|
||||||
|
<option name="SCRIPT_PATH" value="$PROJECT_DIR$/test-ssg-project/bin/ssg" />
|
||||||
|
<option name="SCRIPT_OPTIONS" value="--debug build -g --dry-run -b default" />
|
||||||
|
<option name="INDEPENDENT_SCRIPT_WORKING_DIRECTORY" value="true" />
|
||||||
|
<option name="SCRIPT_WORKING_DIRECTORY" value="$PROJECT_DIR$/test-ssg-project" />
|
||||||
|
<option name="INDEPENDENT_INTERPRETER_PATH" value="true" />
|
||||||
|
<option name="INTERPRETER_PATH" value="/usr/bin/env" />
|
||||||
|
<option name="INTERPRETER_OPTIONS" value="" />
|
||||||
|
<option name="EXECUTE_IN_TERMINAL" value="true" />
|
||||||
|
<option name="EXECUTE_SCRIPT_FILE" value="true" />
|
||||||
|
<envs />
|
||||||
|
<method v="2">
|
||||||
|
<option name="Gradle.BeforeRunTask" enabled="true" tasks="publishToMavenLocal" externalProjectPath="$PROJECT_DIR$" vmOptions="" scriptParameters="" />
|
||||||
|
</method>
|
||||||
|
</configuration>
|
||||||
|
</component>
|
19
.run/run-test-project.run.xml
Normal file
19
.run/run-test-project.run.xml
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
<component name="ProjectRunConfigurationManager">
|
||||||
|
<configuration default="false" name="run-test-project" type="ShConfigurationType">
|
||||||
|
<option name="SCRIPT_TEXT" value="" />
|
||||||
|
<option name="INDEPENDENT_SCRIPT_PATH" value="true" />
|
||||||
|
<option name="SCRIPT_PATH" value="$PROJECT_DIR$/test-ssg-project/bin/ssg" />
|
||||||
|
<option name="SCRIPT_OPTIONS" value="build --log-level=debug --gradle --dry-run --build default" />
|
||||||
|
<option name="INDEPENDENT_SCRIPT_WORKING_DIRECTORY" value="true" />
|
||||||
|
<option name="SCRIPT_WORKING_DIRECTORY" value="$PROJECT_DIR$/test-ssg-project" />
|
||||||
|
<option name="INDEPENDENT_INTERPRETER_PATH" value="true" />
|
||||||
|
<option name="INTERPRETER_PATH" value="/usr/bin/env" />
|
||||||
|
<option name="INTERPRETER_OPTIONS" value="" />
|
||||||
|
<option name="EXECUTE_IN_TERMINAL" value="false" />
|
||||||
|
<option name="EXECUTE_SCRIPT_FILE" value="true" />
|
||||||
|
<envs />
|
||||||
|
<method v="2">
|
||||||
|
<option name="Gradle.BeforeRunTask" enabled="true" tasks="publishToMavenLocal" externalProjectPath="$PROJECT_DIR$" vmOptions="" scriptParameters="" />
|
||||||
|
</method>
|
||||||
|
</configuration>
|
||||||
|
</component>
|
@ -3,6 +3,7 @@ plugins {
|
|||||||
id 'groovy'
|
id 'groovy'
|
||||||
id 'java-library'
|
id 'java-library'
|
||||||
id 'java-test-fixtures'
|
id 'java-test-fixtures'
|
||||||
|
id 'maven-publish'
|
||||||
}
|
}
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
@ -18,26 +19,26 @@ configurations {
|
|||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
api libs.groovy
|
api libs.groovy
|
||||||
|
api libs.groovy.yaml
|
||||||
api libs.groowt.all
|
api libs.groowt.all
|
||||||
|
|
||||||
compileOnlyApi libs.jetbrains.anontations
|
compileOnlyApi libs.jetbrains.anontations
|
||||||
|
|
||||||
implementation libs.groovy.templates
|
implementation libs.classgraph
|
||||||
implementation libs.commonmark
|
implementation libs.commonmark
|
||||||
implementation libs.commonmark.frontmatter
|
implementation libs.commonmark.frontmatter
|
||||||
implementation libs.jsoup
|
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 {
|
jar {
|
||||||
archivesBaseName = 'ssg-api'
|
archivesBaseName = 'ssg-api'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
publishing {
|
||||||
|
publications {
|
||||||
|
create('ssgApi', MavenPublication) {
|
||||||
|
artifactId = 'ssg-api'
|
||||||
|
from components.java
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -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<URL> buildScriptClassLoaderUrls
|
|
||||||
private final @Nullable File buildScript
|
|
||||||
private final Collection<BuildSpec> 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 <code>null</code>.
|
|
||||||
*/
|
|
||||||
BuildScriptBasedStaticSiteGenerator(
|
|
||||||
Collection<URL> buildScriptClassLoaderUrls,
|
|
||||||
@Nullable File buildScript = null
|
|
||||||
) {
|
|
||||||
this.buildScriptClassLoaderUrls = buildScriptClassLoaderUrls
|
|
||||||
this.buildScript = buildScript
|
|
||||||
}
|
|
||||||
|
|
||||||
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 = 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<BuildSpec> builds
|
|
||||||
final Collection<Diagnostic> diagnostics
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: cache build script results
|
|
||||||
@Override
|
|
||||||
boolean doBuild(
|
|
||||||
String buildName,
|
|
||||||
Collection<BuildScriptConfiguratorFactory> configuratorFactories,
|
|
||||||
Map<String, Object> buildScriptArgs,
|
|
||||||
Consumer<Collection<Diagnostic>> 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
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -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<Collection<Task>> convert(BuildSpec buildScriptResult)
|
|
||||||
}
|
|
@ -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<Text> 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<Text>
|
||||||
|
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<Diagnostic> doBuild(
|
||||||
|
File projectDir,
|
||||||
|
String buildName,
|
||||||
|
String buildScriptFqn,
|
||||||
|
Map<String, String> 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<Page>
|
||||||
|
|
||||||
|
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<Class<? extends PageFactory>>
|
||||||
|
|
||||||
|
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<Diagnostic>
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -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<Collection<Task>> convert(BuildSpec build) {
|
|
||||||
def taskSpec = new TaskSpec(
|
|
||||||
build.name,
|
|
||||||
build.outputDirFunction.apply(build).asFile(),
|
|
||||||
build.siteSpec,
|
|
||||||
build.globals
|
|
||||||
)
|
|
||||||
Collection<Task> tasks = []
|
|
||||||
Collection<Diagnostic> 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)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -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<SiteSpec> 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 })"
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
13
api/src/main/groovy/com/jessebrault/ssg/SsgException.groovy
Normal file
13
api/src/main/groovy/com/jessebrault/ssg/SsgException.groovy
Normal file
@ -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)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,15 +1,12 @@
|
|||||||
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
|
|
||||||
|
|
||||||
interface StaticSiteGenerator {
|
interface StaticSiteGenerator {
|
||||||
boolean doBuild(
|
Collection<Diagnostic> doBuild(
|
||||||
|
File projectDir,
|
||||||
String buildName,
|
String buildName,
|
||||||
Collection<BuildScriptConfiguratorFactory> configuratorFactories,
|
String buildScriptFqn,
|
||||||
Map<String, Object> buildScriptArgs,
|
Map<String, String> buildScriptCliArgs
|
||||||
Consumer<Collection<Diagnostic>> diagnosticsConsumer
|
|
||||||
)
|
)
|
||||||
}
|
}
|
@ -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<File> textsDirs
|
|
||||||
final NamedSetProvider<Page> 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
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -13,26 +13,26 @@ import static java.util.Objects.requireNonNull
|
|||||||
@SuppressWarnings('unused')
|
@SuppressWarnings('unused')
|
||||||
abstract class BuildScriptBase extends Script {
|
abstract class BuildScriptBase extends Script {
|
||||||
|
|
||||||
|
/* --- Logging --- */
|
||||||
|
|
||||||
static final Logger logger = LoggerFactory.getLogger(BuildScriptBase)
|
static final Logger logger = LoggerFactory.getLogger(BuildScriptBase)
|
||||||
static final Marker enter = MarkerFactory.getMarker('ENTER')
|
static final Marker enter = MarkerFactory.getMarker('ENTER')
|
||||||
static final Marker exit = MarkerFactory.getMarker('EXIT')
|
static final Marker exit = MarkerFactory.getMarker('EXIT')
|
||||||
|
|
||||||
|
/* --- build script proper --- */
|
||||||
|
|
||||||
private String extending
|
private String extending
|
||||||
private Closure buildClosure
|
private Closure buildClosure = { }
|
||||||
private File projectRoot
|
private File projectRoot
|
||||||
|
|
||||||
File getProjectRoot() {
|
/* --- Instance DSL helpers --- */
|
||||||
requireNonNull(this.projectRoot)
|
|
||||||
}
|
|
||||||
|
|
||||||
void setProjectRoot(File projectRoot) {
|
|
||||||
this.projectRoot = requireNonNull(projectRoot)
|
|
||||||
}
|
|
||||||
|
|
||||||
File file(String name) {
|
File file(String name) {
|
||||||
new File(this.projectRoot, name)
|
new File(this.projectRoot, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* --- DSL --- */
|
||||||
|
|
||||||
void build(@Nullable String extending, @DelegatesTo(value = BuildDelegate) Closure buildClosure) {
|
void build(@Nullable String extending, @DelegatesTo(value = BuildDelegate) Closure buildClosure) {
|
||||||
this.extending = extending
|
this.extending = extending
|
||||||
this.buildClosure = buildClosure
|
this.buildClosure = buildClosure
|
||||||
@ -43,8 +43,21 @@ abstract class BuildScriptBase extends Script {
|
|||||||
this.buildClosure = buildClosure
|
this.buildClosure = buildClosure
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* --- internal --- */
|
||||||
|
|
||||||
@ApiStatus.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
|
this.extending
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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<String, String> 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<BuildScriptBase>
|
||||||
|
def buildScript = scriptClass.getConstructor().newInstance()
|
||||||
|
buildScript.binding = new Binding(this.scriptCliArgs)
|
||||||
|
buildScript.projectRoot = projectDir
|
||||||
|
buildScript.run()
|
||||||
|
buildScript
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -8,29 +8,32 @@ import java.util.function.Supplier
|
|||||||
|
|
||||||
@NullCheck
|
@NullCheck
|
||||||
@TupleConstructor(includeFields = true)
|
@TupleConstructor(includeFields = true)
|
||||||
class BuildDelegateToBuildSpecConverter {
|
class BuildScriptToBuildSpecConverter {
|
||||||
|
|
||||||
private final FileBuildScriptGetter buildScriptGetter
|
private final BuildScriptGetter buildScriptGetter
|
||||||
private final Supplier<BuildDelegate> buildDelegateSupplier
|
private final Supplier<BuildDelegate> buildDelegateSupplier
|
||||||
|
|
||||||
protected BuildSpec getFromDelegate(String name, BuildDelegate delegate) {
|
protected BuildSpec getFromDelegate(String name, BuildDelegate delegate) {
|
||||||
new BuildSpec(
|
new BuildSpec(
|
||||||
name: name,
|
name: name,
|
||||||
|
basePackages: delegate.basePackages,
|
||||||
siteName: delegate.siteName,
|
siteName: delegate.siteName,
|
||||||
baseUrl: delegate.baseUrl,
|
baseUrl: delegate.baseUrl,
|
||||||
outputDir: delegate.outputDir,
|
outputDir: delegate.outputDir,
|
||||||
globals: delegate.globals,
|
globals: delegate.globals,
|
||||||
|
models: delegate.models,
|
||||||
textsDirs: delegate.textsDirs,
|
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<BuildScriptBase> buildHierarchy = new LinkedList<>()
|
final Deque<BuildScriptBase> buildHierarchy = new LinkedList<>()
|
||||||
buildHierarchy.push(buildScript)
|
buildHierarchy.push(buildScript)
|
||||||
String extending = buildScript.extending
|
String extending = buildScript.extending
|
||||||
while (extending != null) {
|
while (extending != null) {
|
||||||
def from = this.buildScriptGetter.getBuildScript(extending)
|
def from = this.buildScriptGetter.getAndRunBuildScript(extending)
|
||||||
buildHierarchy.push(from)
|
buildHierarchy.push(from)
|
||||||
extending = from.extending
|
extending = from.extending
|
||||||
}
|
}
|
||||||
@ -45,4 +48,9 @@ class BuildDelegateToBuildSpecConverter {
|
|||||||
this.getFromDelegate(name, delegate)
|
this.getFromDelegate(name, delegate)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BuildSpec convert(String buildScriptFqn) {
|
||||||
|
def start = this.buildScriptGetter.getAndRunBuildScript(buildScriptFqn)
|
||||||
|
this.doConvert(buildScriptFqn, start)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -1,37 +1,48 @@
|
|||||||
package com.jessebrault.ssg.buildscript
|
package com.jessebrault.ssg.buildscript
|
||||||
|
|
||||||
import com.jessebrault.ssg.model.Model
|
import com.jessebrault.ssg.model.Model
|
||||||
|
import com.jessebrault.ssg.text.TextConverter
|
||||||
import groovy.transform.EqualsAndHashCode
|
import groovy.transform.EqualsAndHashCode
|
||||||
import groovy.transform.NullCheck
|
import groovy.transform.NullCheck
|
||||||
import groowt.util.fp.provider.NamedProvider
|
import groowt.util.di.RegistryObjectFactory
|
||||||
import groowt.util.fp.provider.Provider
|
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)
|
@NullCheck(includeGenerated = true)
|
||||||
@EqualsAndHashCode
|
@EqualsAndHashCode
|
||||||
final class BuildSpec {
|
final class BuildSpec {
|
||||||
|
|
||||||
final String name
|
final String name
|
||||||
|
final Provider<Set<String>> basePackages
|
||||||
final Provider<String> siteName
|
final Provider<String> siteName
|
||||||
final Provider<String> baseUrl
|
final Provider<String> baseUrl
|
||||||
final Provider<File> outputDir
|
final Provider<File> outputDir
|
||||||
final Provider<Map<String, Object>> globals
|
final Provider<Map<String, Object>> globals
|
||||||
final Set<Provider<File>> textsDirs
|
final Provider<Set<Model>> models
|
||||||
|
final Provider<Set<File>> textsDirs
|
||||||
|
final Provider<Set<TextConverter>> textConverters
|
||||||
|
final Provider<RegistryObjectFactory.Builder> objectFactoryBuilder
|
||||||
|
|
||||||
@SuppressWarnings('GroovyAssignabilityCheck')
|
@SuppressWarnings('GroovyAssignabilityCheck')
|
||||||
BuildSpec(Map args) {
|
BuildSpec(Map args) {
|
||||||
this.name = requireString(args.name)
|
this.name = requireString(args.name)
|
||||||
|
this.basePackages = requireProvider(args.basePackages)
|
||||||
this.siteName = requireProvider(args.siteName)
|
this.siteName = requireProvider(args.siteName)
|
||||||
this.baseUrl = requireProvider(args.baseUrl)
|
this.baseUrl = requireProvider(args.baseUrl)
|
||||||
this.outputDir = requireProvider(args.outputDir)
|
this.outputDir = requireProvider(args.outputDir)
|
||||||
this.globals = requireMap(args.globals)
|
this.globals = requireProvider(args.globals)
|
||||||
this.textsDirs = requireSet(args.textsDirs)
|
this.models = requireProvider(args.models)
|
||||||
|
this.textsDirs = requireProvider(args.textsDirs)
|
||||||
|
this.textConverters = requireProvider(args.textConverters)
|
||||||
|
this.objectFactoryBuilder = requireProvider(args.objectFactoryBuilder)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
String toString() {
|
String toString() {
|
||||||
"Build(name: ${ this.name })"
|
"Build(name: ${this.name}, basePackages: $basePackages, siteName: $siteName, " +
|
||||||
|
"baseUrl: $baseUrl, outputDir: $outputDir, textsDirs: $textsDirs)"
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,19 +0,0 @@
|
|||||||
package com.jessebrault.ssg.buildscript
|
|
||||||
|
|
||||||
import groovy.transform.NullCheck
|
|
||||||
import groovy.transform.TupleConstructor
|
|
||||||
|
|
||||||
@NullCheck
|
|
||||||
@TupleConstructor(includeFields = true)
|
|
||||||
final class FileBuildScriptGetter {
|
|
||||||
|
|
||||||
private final GroovyClassLoader groovyClassLoader
|
|
||||||
|
|
||||||
BuildScriptBase getBuildScript(String name) {
|
|
||||||
Class<?> scriptClass = this.groovyClassLoader.loadClass(name, true, false)
|
|
||||||
def scriptObject = scriptClass.getConstructor().newInstance()
|
|
||||||
assert scriptObject instanceof BuildScriptBase
|
|
||||||
scriptObject
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,37 +1,61 @@
|
|||||||
package com.jessebrault.ssg.buildscript.delegates
|
package com.jessebrault.ssg.buildscript.delegates
|
||||||
|
|
||||||
import groovy.transform.EqualsAndHashCode
|
import com.jessebrault.ssg.model.Model
|
||||||
import groovy.transform.NullCheck
|
import com.jessebrault.ssg.model.Models
|
||||||
|
import com.jessebrault.ssg.text.MarkdownTextConverter
|
||||||
|
import com.jessebrault.ssg.text.TextConverter
|
||||||
import groowt.util.di.DefaultRegistryObjectFactory
|
import groowt.util.di.DefaultRegistryObjectFactory
|
||||||
import groowt.util.di.RegistryObjectFactory
|
import groowt.util.di.RegistryObjectFactory
|
||||||
|
import groowt.util.fp.property.DefaultProperty
|
||||||
import groowt.util.fp.property.Property
|
import groowt.util.fp.property.Property
|
||||||
import groowt.util.fp.provider.DefaultSetProvider
|
import groowt.util.fp.provider.DefaultProvider
|
||||||
|
import groowt.util.fp.provider.NamedProvider
|
||||||
import groowt.util.fp.provider.Provider
|
import groowt.util.fp.provider.Provider
|
||||||
import groowt.util.fp.provider.SetProvider
|
|
||||||
|
|
||||||
import java.util.function.Supplier
|
import java.util.function.Supplier
|
||||||
|
|
||||||
@NullCheck(includeGenerated = true)
|
|
||||||
@EqualsAndHashCode(includeFields = true)
|
|
||||||
final class BuildDelegate {
|
final class BuildDelegate {
|
||||||
|
|
||||||
static Supplier<BuildDelegate> withDefaults() {
|
static Supplier<BuildDelegate> withDefaults(File projectDir) {
|
||||||
return {
|
return {
|
||||||
new BuildDelegate().tap {
|
new BuildDelegate(projectDir).tap {
|
||||||
outputDir.convention = 'dist'
|
basePackages.convention = [] as Set<String>
|
||||||
|
outputDir.convention = new File(projectDir, 'dist')
|
||||||
globals.convention = [:]
|
globals.convention = [:]
|
||||||
objectFactory.convention = DefaultRegistryObjectFactory.Builder.withDefaults()
|
models.convention = [] as Set<Model>
|
||||||
|
textsDirs.convention = [new File(projectDir, 'texts')] as Set<File>
|
||||||
|
textConverters.convention = [new MarkdownTextConverter()] as Set<TextConverter>
|
||||||
|
objectFactoryBuilder.convention = DefaultRegistryObjectFactory.Builder.withDefaults()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final Property<String> siteName = Property.empty()
|
final File projectDir
|
||||||
final Property<String> baseUrl = Property.empty()
|
|
||||||
final Property<File> outputDir = Property.empty()
|
|
||||||
final Property<Map<String, Object>> globals = Property.empty()
|
|
||||||
final Property<RegistryObjectFactory> objectFactory = Property.empty()
|
|
||||||
|
|
||||||
private final Set<Provider<File>> textsDirs = []
|
final Property<Set<String>> basePackages = DefaultProperty.<Set<String>>empty(Set)
|
||||||
|
final Property<String> siteName = DefaultProperty.empty(String)
|
||||||
|
final Property<String> baseUrl = DefaultProperty.empty(String)
|
||||||
|
final Property<File> outputDir = DefaultProperty.empty(File)
|
||||||
|
final Property<Map<String, Object>> globals = DefaultProperty.<Map<String, Object>>empty(Map)
|
||||||
|
final Property<Set<Model>> models = DefaultProperty.<Set<Model>>empty(Set)
|
||||||
|
final Property<Set<File>> textsDirs = DefaultProperty.<Set<File>>empty(Set)
|
||||||
|
final Property<Set<TextConverter>> textConverters = DefaultProperty.<Set<TextConverter>>empty(Set)
|
||||||
|
final Property<RegistryObjectFactory.Builder> objectFactoryBuilder =
|
||||||
|
DefaultProperty.empty(RegistryObjectFactory.Builder)
|
||||||
|
|
||||||
|
private BuildDelegate(File projectDir) {
|
||||||
|
this.projectDir = projectDir
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TODO: add friendly DSL methods for setting all properties */
|
||||||
|
|
||||||
|
void basePackage(String toAdd) {
|
||||||
|
this.basePackages.configure { it.add(toAdd) }
|
||||||
|
}
|
||||||
|
|
||||||
|
void basePackages(String... toAdd) {
|
||||||
|
toAdd.each { this.basePackage(it) }
|
||||||
|
}
|
||||||
|
|
||||||
void siteName(String siteName) {
|
void siteName(String siteName) {
|
||||||
this.siteName.set(siteName)
|
this.siteName.set(siteName)
|
||||||
@ -62,25 +86,49 @@ final class BuildDelegate {
|
|||||||
globalsClosure.delegate = globalsDelegate
|
globalsClosure.delegate = globalsDelegate
|
||||||
globalsClosure.resolveStrategy = Closure.DELEGATE_FIRST
|
globalsClosure.resolveStrategy = Closure.DELEGATE_FIRST
|
||||||
globalsClosure()
|
globalsClosure()
|
||||||
this.globals.set {
|
this.globals.set DefaultProvider.ofLazy(Map) {
|
||||||
this.globals.get() + globalsDelegate
|
this.globals.get() + globalsDelegate
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void model(String name, Object obj) {
|
||||||
|
this.models.configure {it.add(Models.of(name, obj)) }
|
||||||
|
}
|
||||||
|
|
||||||
|
void model(Model model) {
|
||||||
|
this.models.configure { it.add(model) }
|
||||||
|
}
|
||||||
|
|
||||||
|
void model(String name, Provider tProvider) {
|
||||||
|
this.models.configure { it.add(Models.ofProvider(name, tProvider)) }
|
||||||
|
}
|
||||||
|
|
||||||
|
<T> void model(String name, Class<T> type, Supplier<? extends T> tSupplier) {
|
||||||
|
this.models.configure { it.add(Models.ofSupplier(name, type, tSupplier)) }
|
||||||
|
}
|
||||||
|
|
||||||
|
void model(NamedProvider namedProvider) {
|
||||||
|
this.models.configure { it.add(Models.ofNamedProvider(namedProvider)) }
|
||||||
|
}
|
||||||
|
|
||||||
void textsDir(File textsDir) {
|
void textsDir(File textsDir) {
|
||||||
this.textsDirs.add { textsDir }
|
this.textsDirs.configure { it.add(textsDir) }
|
||||||
}
|
}
|
||||||
|
|
||||||
void textsDir(Provider<File> textsDirProvider) {
|
void textsDirs(File... textsDirs) {
|
||||||
this.textsDirs << textsDirProvider
|
textsDirs.each { this.textsDir(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
void textsDirs(Set<Provider<File>> textsDirProviders) {
|
void textConverter(TextConverter textConverter) {
|
||||||
textsDirProviders.each { this.textsDir(it) }
|
this.textConverters.configure { it.add(textConverter) }
|
||||||
}
|
}
|
||||||
|
|
||||||
SetProvider<File> getTextsDirs() {
|
void textConverters(TextConverter... textConverters) {
|
||||||
new DefaultSetProvider(this.textsDirs)
|
textConverters.each { this.textConverter(it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
void objectFactoryBuilder(RegistryObjectFactory.Builder builder) {
|
||||||
|
this.objectFactoryBuilder.set(builder)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
15
api/src/main/groovy/com/jessebrault/ssg/di/Global.groovy
Normal file
15
api/src/main/groovy/com/jessebrault/ssg/di/Global.groovy
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
package com.jessebrault.ssg.di
|
||||||
|
|
||||||
|
import jakarta.inject.Qualifier
|
||||||
|
|
||||||
|
import java.lang.annotation.ElementType
|
||||||
|
import java.lang.annotation.Retention
|
||||||
|
import java.lang.annotation.RetentionPolicy
|
||||||
|
import java.lang.annotation.Target
|
||||||
|
|
||||||
|
@Qualifier
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Target([ElementType.PARAMETER, ElementType.METHOD, ElementType.FIELD])
|
||||||
|
@interface Global {
|
||||||
|
String value()
|
||||||
|
}
|
@ -0,0 +1,43 @@
|
|||||||
|
package com.jessebrault.ssg.di
|
||||||
|
|
||||||
|
import groovy.transform.TupleConstructor
|
||||||
|
import groowt.util.di.Binding
|
||||||
|
import groowt.util.di.QualifierHandler
|
||||||
|
import groowt.util.di.QualifierHandlerContainer
|
||||||
|
import groowt.util.di.RegistryExtension
|
||||||
|
import groowt.util.di.SingletonBinding
|
||||||
|
|
||||||
|
import java.lang.annotation.Annotation
|
||||||
|
|
||||||
|
class GlobalsExtension implements QualifierHandlerContainer, RegistryExtension {
|
||||||
|
|
||||||
|
@TupleConstructor(includeFields = true)
|
||||||
|
static class GlobalQualifierHandler implements QualifierHandler<Global> {
|
||||||
|
|
||||||
|
private final GlobalsExtension extension
|
||||||
|
|
||||||
|
@Override
|
||||||
|
<T> Binding<T> handle(Global global, Class<T> aClass) {
|
||||||
|
if (extension.globals.containsKey(global.value())) {
|
||||||
|
return new SingletonBinding<T>(extension.globals.get(global.value()) as T)
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException("There is no global for ${global.value()}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
final Map<String, Object> globals = [:]
|
||||||
|
|
||||||
|
private final GlobalQualifierHandler globalQualifierHandler = new GlobalQualifierHandler(this)
|
||||||
|
|
||||||
|
@Override
|
||||||
|
<A extends Annotation> QualifierHandler<A> getQualifierHandler(Class<A> aClass) {
|
||||||
|
if (Global.is(aClass)) {
|
||||||
|
return this.globalQualifierHandler as QualifierHandler<A>
|
||||||
|
} else {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,15 @@
|
|||||||
|
package com.jessebrault.ssg.di
|
||||||
|
|
||||||
|
import jakarta.inject.Qualifier
|
||||||
|
|
||||||
|
import java.lang.annotation.ElementType
|
||||||
|
import java.lang.annotation.Retention
|
||||||
|
import java.lang.annotation.RetentionPolicy
|
||||||
|
import java.lang.annotation.Target
|
||||||
|
|
||||||
|
@Qualifier
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Target([ElementType.PARAMETER, ElementType.METHOD, ElementType.FIELD])
|
||||||
|
@interface InjectModel {
|
||||||
|
String value()
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
package com.jessebrault.ssg.di
|
||||||
|
|
||||||
|
import groovy.transform.TupleConstructor
|
||||||
|
import groowt.util.di.Binding
|
||||||
|
import groowt.util.di.QualifierHandler
|
||||||
|
import groowt.util.di.SingletonBinding
|
||||||
|
|
||||||
|
@TupleConstructor(includeFields = true)
|
||||||
|
class InjectModelQualifierHandler implements QualifierHandler<InjectModel> {
|
||||||
|
|
||||||
|
private final ModelsExtension extension
|
||||||
|
|
||||||
|
@Override
|
||||||
|
<T> Binding<T> handle(InjectModel injectModel, Class<T> requestedClass) {
|
||||||
|
def found = this.extension.allModels.find {
|
||||||
|
requestedClass.isAssignableFrom(it.class) && it.name == injectModel.value()
|
||||||
|
}
|
||||||
|
if (found == null) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"Could not find a Model with name ${injectModel.value()} and/or type $requestedClass.name"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
new SingletonBinding<T>(found.get() as T)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,15 @@
|
|||||||
|
package com.jessebrault.ssg.di
|
||||||
|
|
||||||
|
import jakarta.inject.Qualifier
|
||||||
|
|
||||||
|
import java.lang.annotation.ElementType
|
||||||
|
import java.lang.annotation.Retention
|
||||||
|
import java.lang.annotation.RetentionPolicy
|
||||||
|
import java.lang.annotation.Target
|
||||||
|
|
||||||
|
@Qualifier
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Target([ElementType.PARAMETER, ElementType.METHOD, ElementType.FIELD])
|
||||||
|
@interface InjectModels {
|
||||||
|
String[] value()
|
||||||
|
}
|
@ -0,0 +1,27 @@
|
|||||||
|
package com.jessebrault.ssg.di
|
||||||
|
|
||||||
|
import groovy.transform.TupleConstructor
|
||||||
|
import groowt.util.di.Binding
|
||||||
|
import groowt.util.di.QualifierHandler
|
||||||
|
import groowt.util.di.SingletonBinding
|
||||||
|
|
||||||
|
@TupleConstructor(includeFields = true)
|
||||||
|
class InjectModelsQualifierHandler implements QualifierHandler<InjectModels> {
|
||||||
|
|
||||||
|
private final ModelsExtension extension
|
||||||
|
|
||||||
|
@Override
|
||||||
|
<T> Binding<T> handle(InjectModels injectModels, Class<T> requestedType) {
|
||||||
|
if (!List.is(requestedType)) {
|
||||||
|
throw new IllegalArgumentException("@InjectModels must be used with List.")
|
||||||
|
}
|
||||||
|
def allFound = this.extension.allModels.inject([] as T) { acc, model ->
|
||||||
|
if (model.type.isAssignableFrom(requestedType) && model.name in injectModels.value()) {
|
||||||
|
acc << model.get()
|
||||||
|
}
|
||||||
|
acc
|
||||||
|
}
|
||||||
|
new SingletonBinding<T>(allFound as T)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package com.jessebrault.ssg.objects
|
package com.jessebrault.ssg.di
|
||||||
|
|
||||||
import jakarta.inject.Qualifier
|
import jakarta.inject.Qualifier
|
||||||
|
|
@ -1,4 +1,4 @@
|
|||||||
package com.jessebrault.ssg.objects
|
package com.jessebrault.ssg.di
|
||||||
|
|
||||||
import com.jessebrault.ssg.page.Page
|
import com.jessebrault.ssg.page.Page
|
||||||
import groovy.transform.TupleConstructor
|
import groovy.transform.TupleConstructor
|
||||||
@ -17,7 +17,7 @@ class InjectPageQualifierHandler implements QualifierHandler<InjectPage> {
|
|||||||
throw new IllegalArgumentException("Cannot inject a Page into a non-Page parameter/setter/field.")
|
throw new IllegalArgumentException("Cannot inject a Page into a non-Page parameter/setter/field.")
|
||||||
}
|
}
|
||||||
def requested = injectPage.value()
|
def requested = injectPage.value()
|
||||||
def found = this.pagesExtension.pageProviders.find {
|
def found = this.pagesExtension.allPages.find {
|
||||||
if (requested.startsWith('/')) {
|
if (requested.startsWith('/')) {
|
||||||
it.path == requested
|
it.path == requested
|
||||||
} else {
|
} else {
|
||||||
@ -27,7 +27,7 @@ class InjectPageQualifierHandler implements QualifierHandler<InjectPage> {
|
|||||||
if (found == null) {
|
if (found == null) {
|
||||||
throw new IllegalArgumentException("Cannot find a page with the following name or path: $requested")
|
throw new IllegalArgumentException("Cannot find a page with the following name or path: $requested")
|
||||||
}
|
}
|
||||||
new SingletonBinding<T>(found.get() as T)
|
new SingletonBinding<T>(found as T)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package com.jessebrault.ssg.objects
|
package com.jessebrault.ssg.di
|
||||||
|
|
||||||
import jakarta.inject.Qualifier
|
import jakarta.inject.Qualifier
|
||||||
|
|
@ -1,4 +1,4 @@
|
|||||||
package com.jessebrault.ssg.objects
|
package com.jessebrault.ssg.di
|
||||||
|
|
||||||
import com.jessebrault.ssg.page.Page
|
import com.jessebrault.ssg.page.Page
|
||||||
import com.jessebrault.ssg.util.Glob
|
import com.jessebrault.ssg.util.Glob
|
||||||
@ -23,19 +23,19 @@ class InjectPagesQualifierHandler implements QualifierHandler<InjectPages> {
|
|||||||
for (final String requested : injectPages.value()) {
|
for (final String requested : injectPages.value()) {
|
||||||
if (requested.startsWith('/')) {
|
if (requested.startsWith('/')) {
|
||||||
def glob = new Glob(requested)
|
def glob = new Glob(requested)
|
||||||
def allFound = this.pagesExtension.pageProviders.inject([] as Set<Page>) { acc, pageProvider ->
|
def allFound = this.pagesExtension.allPages.inject([] as Set<Page>) { acc, page ->
|
||||||
if (glob.matches(pageProvider.path)) {
|
if (glob.matches(page.path)) {
|
||||||
acc << pageProvider.get()
|
acc << page
|
||||||
}
|
}
|
||||||
acc
|
acc
|
||||||
}
|
}
|
||||||
allFound.each { foundPages << it }
|
allFound.each { foundPages << it }
|
||||||
} else {
|
} else {
|
||||||
def found = this.pagesExtension.pageProviders.find { it.name == requested }
|
def found = this.pagesExtension.allPages.find { it.name == requested }
|
||||||
if (found == null) {
|
if (found == null) {
|
||||||
throw new IllegalArgumentException("Cannot find page with the name: $requested")
|
throw new IllegalArgumentException("Cannot find page with the name: $requested")
|
||||||
}
|
}
|
||||||
foundPages << found.get()
|
foundPages << found
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
new SingletonBinding<T>(foundPages as T)
|
new SingletonBinding<T>(foundPages as T)
|
@ -1,4 +1,4 @@
|
|||||||
package com.jessebrault.ssg.objects
|
package com.jessebrault.ssg.di
|
||||||
|
|
||||||
import jakarta.inject.Qualifier
|
import jakarta.inject.Qualifier
|
||||||
|
|
@ -0,0 +1,28 @@
|
|||||||
|
package com.jessebrault.ssg.di
|
||||||
|
|
||||||
|
import com.jessebrault.ssg.text.Text
|
||||||
|
import groovy.transform.TupleConstructor
|
||||||
|
import groowt.util.di.Binding
|
||||||
|
import groowt.util.di.QualifierHandler
|
||||||
|
import groowt.util.di.SingletonBinding
|
||||||
|
|
||||||
|
@TupleConstructor(includeFields = true)
|
||||||
|
class InjectTextQualifierHandler implements QualifierHandler<InjectText> {
|
||||||
|
|
||||||
|
private final TextsExtension extension
|
||||||
|
|
||||||
|
@Override
|
||||||
|
<T> Binding<T> handle(InjectText injectText, Class<T> requestedClass) {
|
||||||
|
if (!Text.isAssignableFrom(requestedClass)) {
|
||||||
|
throw new IllegalArgumentException("Cannot @InjectText on a non-Text parameter/method/field.")
|
||||||
|
}
|
||||||
|
def found = this.extension.allTexts.find {
|
||||||
|
it.name == injectText.value() || it.path == injectText.value()
|
||||||
|
}
|
||||||
|
if (found == null) {
|
||||||
|
throw new IllegalArgumentException("Could not find a Text with name or path ${injectText.value()}")
|
||||||
|
}
|
||||||
|
new SingletonBinding<T>(found as T)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package com.jessebrault.ssg.objects
|
package com.jessebrault.ssg.di
|
||||||
|
|
||||||
import jakarta.inject.Qualifier
|
import jakarta.inject.Qualifier
|
||||||
|
|
@ -0,0 +1,39 @@
|
|||||||
|
package com.jessebrault.ssg.di
|
||||||
|
|
||||||
|
import com.jessebrault.ssg.text.Text
|
||||||
|
import com.jessebrault.ssg.util.Glob
|
||||||
|
import groovy.transform.TupleConstructor
|
||||||
|
import groowt.util.di.Binding
|
||||||
|
import groowt.util.di.QualifierHandler
|
||||||
|
import groowt.util.di.SingletonBinding
|
||||||
|
|
||||||
|
@TupleConstructor(includeFields = true)
|
||||||
|
class InjectTextsQualifierHandler implements QualifierHandler<InjectTexts> {
|
||||||
|
|
||||||
|
private final TextsExtension extension
|
||||||
|
|
||||||
|
@Override
|
||||||
|
<T> Binding<T> handle(InjectTexts injectTexts, Class<T> aClass) {
|
||||||
|
if (!Set.is(aClass)) {
|
||||||
|
throw new IllegalArgumentException('Cannot @InjectTexts on a non-Set parameter/method/field.')
|
||||||
|
}
|
||||||
|
def allFound = injectTexts.value().inject([] as Set<Text>) { acc, nameOrPathGlob ->
|
||||||
|
if (nameOrPathGlob.startsWith('/')) {
|
||||||
|
def glob = new Glob(nameOrPathGlob)
|
||||||
|
def matching = this.extension.allTexts.inject([] as Set<Text>) { matchingAcc, text ->
|
||||||
|
if (glob.matches(text.path)) {
|
||||||
|
matchingAcc << text
|
||||||
|
}
|
||||||
|
matchingAcc
|
||||||
|
}
|
||||||
|
acc.addAll(matching)
|
||||||
|
} else {
|
||||||
|
def found = this.extension.allTexts.find { it.name == nameOrPathGlob }
|
||||||
|
acc << found
|
||||||
|
}
|
||||||
|
acc
|
||||||
|
}
|
||||||
|
new SingletonBinding<T>(allFound as T)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,28 @@
|
|||||||
|
package com.jessebrault.ssg.di
|
||||||
|
|
||||||
|
import com.jessebrault.ssg.model.Model
|
||||||
|
import groowt.util.di.QualifierHandler
|
||||||
|
import groowt.util.di.QualifierHandlerContainer
|
||||||
|
import groowt.util.di.RegistryExtension
|
||||||
|
|
||||||
|
import java.lang.annotation.Annotation
|
||||||
|
|
||||||
|
class ModelsExtension implements QualifierHandlerContainer, RegistryExtension {
|
||||||
|
|
||||||
|
final Set<Model> allModels = []
|
||||||
|
|
||||||
|
private final QualifierHandler<InjectModel> injectModelQualifierHandler = new InjectModelQualifierHandler(this)
|
||||||
|
private final QualifierHandler<InjectModels> injectModelsQualifierHandler = new InjectModelsQualifierHandler(this)
|
||||||
|
|
||||||
|
@Override
|
||||||
|
<A extends Annotation> QualifierHandler<A> getQualifierHandler(Class<A> aClass) {
|
||||||
|
if (aClass == InjectModel) {
|
||||||
|
return this.injectModelQualifierHandler as QualifierHandler<A>
|
||||||
|
} else if (aClass == InjectModels) {
|
||||||
|
return this.injectModelsQualifierHandler as QualifierHandler<A>
|
||||||
|
} else {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,7 +1,6 @@
|
|||||||
package com.jessebrault.ssg.objects
|
package com.jessebrault.ssg.di
|
||||||
|
|
||||||
|
import com.jessebrault.ssg.page.Page
|
||||||
import com.jessebrault.ssg.provider.PageProvider
|
|
||||||
import groowt.util.di.QualifierHandler
|
import groowt.util.di.QualifierHandler
|
||||||
import groowt.util.di.QualifierHandlerContainer
|
import groowt.util.di.QualifierHandlerContainer
|
||||||
import groowt.util.di.RegistryExtension
|
import groowt.util.di.RegistryExtension
|
||||||
@ -10,7 +9,7 @@ import java.lang.annotation.Annotation
|
|||||||
|
|
||||||
class PagesExtension implements QualifierHandlerContainer, RegistryExtension {
|
class PagesExtension implements QualifierHandlerContainer, RegistryExtension {
|
||||||
|
|
||||||
final Set<PageProvider> pageProviders = []
|
final Set<Page> allPages = []
|
||||||
|
|
||||||
private final QualifierHandler<InjectPage> injectPage = new InjectPageQualifierHandler(this)
|
private final QualifierHandler<InjectPage> injectPage = new InjectPageQualifierHandler(this)
|
||||||
private final QualifierHandler<InjectPages> injectPages = new InjectPagesQualifierHandler(this)
|
private final QualifierHandler<InjectPages> injectPages = new InjectPagesQualifierHandler(this)
|
13
api/src/main/groovy/com/jessebrault/ssg/di/SelfPage.groovy
Normal file
13
api/src/main/groovy/com/jessebrault/ssg/di/SelfPage.groovy
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
package com.jessebrault.ssg.di
|
||||||
|
|
||||||
|
import jakarta.inject.Qualifier
|
||||||
|
|
||||||
|
import java.lang.annotation.ElementType
|
||||||
|
import java.lang.annotation.Retention
|
||||||
|
import java.lang.annotation.RetentionPolicy
|
||||||
|
import java.lang.annotation.Target
|
||||||
|
|
||||||
|
@Qualifier
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Target([ElementType.PARAMETER, ElementType.METHOD, ElementType.FIELD])
|
||||||
|
@interface SelfPage {}
|
@ -0,0 +1,42 @@
|
|||||||
|
package com.jessebrault.ssg.di
|
||||||
|
|
||||||
|
import com.jessebrault.ssg.page.Page
|
||||||
|
import groovy.transform.TupleConstructor
|
||||||
|
import groowt.util.di.*
|
||||||
|
|
||||||
|
import java.lang.annotation.Annotation
|
||||||
|
|
||||||
|
class SelfPageExtension implements RegistryExtension, QualifierHandlerContainer {
|
||||||
|
|
||||||
|
@TupleConstructor(includeFields = true)
|
||||||
|
static class SelfPageQualifierHandler implements QualifierHandler<SelfPage> {
|
||||||
|
|
||||||
|
private final SelfPageExtension extension
|
||||||
|
|
||||||
|
@Override
|
||||||
|
<T> Binding<T> handle(SelfPage selfPage, Class<T> requestedType) {
|
||||||
|
if (!Page.class.isAssignableFrom(requestedType)) {
|
||||||
|
throw new IllegalArgumentException('Cannot put @SelfPage on a non-Page parameter/method/field.')
|
||||||
|
}
|
||||||
|
if (this.extension.currentPage == null) {
|
||||||
|
throw new IllegalStateException('Cannot get @SelfPage because extension.currentPage is null.')
|
||||||
|
}
|
||||||
|
new SingletonBinding<T>(this.extension.currentPage as T)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Page currentPage
|
||||||
|
|
||||||
|
private final SelfPageQualifierHandler selfPageQualifierHandler = new SelfPageQualifierHandler(this)
|
||||||
|
|
||||||
|
@Override
|
||||||
|
<A extends Annotation> QualifierHandler<A> getQualifierHandler(Class<A> annotationType) {
|
||||||
|
if (SelfPage.is(annotationType)) {
|
||||||
|
return this.selfPageQualifierHandler as QualifierHandler<A>
|
||||||
|
} else {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package com.jessebrault.ssg.objects
|
package com.jessebrault.ssg.di
|
||||||
|
|
||||||
import groowt.util.di.DefaultRegistryObjectFactory
|
import groowt.util.di.DefaultRegistryObjectFactory
|
||||||
import groowt.util.di.RegistryObjectFactory
|
import groowt.util.di.RegistryObjectFactory
|
@ -0,0 +1,28 @@
|
|||||||
|
package com.jessebrault.ssg.di
|
||||||
|
|
||||||
|
import com.jessebrault.ssg.text.Text
|
||||||
|
import groowt.util.di.QualifierHandler
|
||||||
|
import groowt.util.di.QualifierHandlerContainer
|
||||||
|
import groowt.util.di.RegistryExtension
|
||||||
|
|
||||||
|
import java.lang.annotation.Annotation
|
||||||
|
|
||||||
|
class TextsExtension implements QualifierHandlerContainer, RegistryExtension {
|
||||||
|
|
||||||
|
final Set<Text> allTexts = []
|
||||||
|
|
||||||
|
private final QualifierHandler<InjectText> injectTextQualifierHandler = new InjectTextQualifierHandler(this)
|
||||||
|
private final QualifierHandler<InjectTexts> injectTextsQualifierHandler = new InjectTextsQualifierHandler(this)
|
||||||
|
|
||||||
|
@Override
|
||||||
|
<A extends Annotation> QualifierHandler<A> getQualifierHandler(Class<A> aClass) {
|
||||||
|
if (InjectText.is(aClass)) {
|
||||||
|
return this.injectTextQualifierHandler as QualifierHandler<A>
|
||||||
|
} else if (InjectTexts.is(aClass)) {
|
||||||
|
return this.injectTextsQualifierHandler as QualifierHandler<A>
|
||||||
|
} else {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,56 +0,0 @@
|
|||||||
package com.jessebrault.ssg.dsl
|
|
||||||
|
|
||||||
import com.jessebrault.ssg.part.Part
|
|
||||||
import com.jessebrault.ssg.render.RenderContext
|
|
||||||
import com.jessebrault.ssg.text.Text
|
|
||||||
import com.jessebrault.ssg.util.Diagnostic
|
|
||||||
import groovy.transform.EqualsAndHashCode
|
|
||||||
import org.jetbrains.annotations.Nullable
|
|
||||||
|
|
||||||
import java.util.function.Consumer
|
|
||||||
|
|
||||||
import static java.util.Objects.requireNonNull
|
|
||||||
|
|
||||||
@EqualsAndHashCode(includeFields = true)
|
|
||||||
final class EmbeddablePart {
|
|
||||||
|
|
||||||
private final Part part
|
|
||||||
private final RenderContext context
|
|
||||||
private final Consumer<Collection<Diagnostic>> diagnosticsConsumer
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
private final Text text
|
|
||||||
|
|
||||||
EmbeddablePart(
|
|
||||||
Part part,
|
|
||||||
RenderContext context,
|
|
||||||
Consumer<Collection<Diagnostic>> diagnosticsConsumer,
|
|
||||||
@Nullable Text text
|
|
||||||
) {
|
|
||||||
this.part = requireNonNull(part)
|
|
||||||
this.context = requireNonNull(context)
|
|
||||||
this.diagnosticsConsumer = requireNonNull(diagnosticsConsumer)
|
|
||||||
this.text = text
|
|
||||||
}
|
|
||||||
|
|
||||||
String render(Map binding = [:]) {
|
|
||||||
def result = this.part.type.renderer.render(
|
|
||||||
this.part,
|
|
||||||
binding,
|
|
||||||
this.context,
|
|
||||||
this.text
|
|
||||||
)
|
|
||||||
if (result.hasDiagnostics()) {
|
|
||||||
this.diagnosticsConsumer.accept(result.diagnostics)
|
|
||||||
''
|
|
||||||
} else {
|
|
||||||
result.get()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
String toString() {
|
|
||||||
"EmbeddablePart(part: ${ this.part })"
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,36 +0,0 @@
|
|||||||
package com.jessebrault.ssg.dsl
|
|
||||||
|
|
||||||
import com.jessebrault.ssg.render.RenderContext
|
|
||||||
import com.jessebrault.ssg.text.Text
|
|
||||||
import com.jessebrault.ssg.util.Diagnostic
|
|
||||||
import groovy.transform.EqualsAndHashCode
|
|
||||||
import org.jetbrains.annotations.Nullable
|
|
||||||
|
|
||||||
import java.util.function.Consumer
|
|
||||||
|
|
||||||
import static java.util.Objects.requireNonNull
|
|
||||||
|
|
||||||
@EqualsAndHashCode(includeFields = true)
|
|
||||||
final class EmbeddablePartsMap {
|
|
||||||
|
|
||||||
@Delegate
|
|
||||||
private final Map<String, EmbeddablePart> partsMap = [:]
|
|
||||||
|
|
||||||
EmbeddablePartsMap(
|
|
||||||
RenderContext context,
|
|
||||||
Consumer<Collection<Diagnostic>> diagnosticsConsumer,
|
|
||||||
@Nullable Text text = null
|
|
||||||
) {
|
|
||||||
requireNonNull(context)
|
|
||||||
requireNonNull(diagnosticsConsumer)
|
|
||||||
context.parts.each {
|
|
||||||
this[it.path] = new EmbeddablePart(it, context, diagnosticsConsumer, text)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
String toString() {
|
|
||||||
"EmbeddablePartsMap(partsMap: ${ this.partsMap })"
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,63 +0,0 @@
|
|||||||
package com.jessebrault.ssg.dsl
|
|
||||||
|
|
||||||
import com.jessebrault.ssg.text.FrontMatter
|
|
||||||
import com.jessebrault.ssg.text.Text
|
|
||||||
import com.jessebrault.ssg.util.Diagnostic
|
|
||||||
import groovy.transform.EqualsAndHashCode
|
|
||||||
import groovy.transform.Memoized
|
|
||||||
import groovy.transform.NullCheck
|
|
||||||
import groovy.transform.TupleConstructor
|
|
||||||
|
|
||||||
import java.util.function.Consumer
|
|
||||||
|
|
||||||
@TupleConstructor(includeFields = true, defaults = false)
|
|
||||||
@NullCheck(includeGenerated = true)
|
|
||||||
@EqualsAndHashCode(includeFields = true)
|
|
||||||
final class EmbeddableText {
|
|
||||||
|
|
||||||
private final Text text
|
|
||||||
private final Consumer<Collection<Diagnostic>> diagnosticsConsumer
|
|
||||||
|
|
||||||
@Memoized
|
|
||||||
String render() {
|
|
||||||
def result = this.text.type.renderer.render(this.text)
|
|
||||||
if (result.diagnostics.size() > 0) {
|
|
||||||
this.diagnosticsConsumer.accept(result.diagnostics)
|
|
||||||
''
|
|
||||||
} else {
|
|
||||||
result.get()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Memoized
|
|
||||||
FrontMatter getFrontMatter() {
|
|
||||||
def result = this.text.type.frontMatterGetter.get(this.text)
|
|
||||||
if (result.hasDiagnostics()) {
|
|
||||||
this.diagnosticsConsumer.accept(result.diagnostics)
|
|
||||||
new FrontMatter(this.text, [:])
|
|
||||||
} else {
|
|
||||||
result.get()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Memoized
|
|
||||||
String getExcerpt(int limit) {
|
|
||||||
def result = this.text.type.excerptGetter.getExcerpt(this.text, limit)
|
|
||||||
if (result.hasDiagnostics()) {
|
|
||||||
this.diagnosticsConsumer.accept(result.diagnostics)
|
|
||||||
''
|
|
||||||
} else {
|
|
||||||
result.get()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
String getPath() {
|
|
||||||
this.text.path
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
String toString() {
|
|
||||||
"EmbeddableText(text: ${ this.text })"
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,28 +0,0 @@
|
|||||||
package com.jessebrault.ssg.dsl
|
|
||||||
|
|
||||||
import com.jessebrault.ssg.text.Text
|
|
||||||
import com.jessebrault.ssg.util.Diagnostic
|
|
||||||
import groovy.transform.EqualsAndHashCode
|
|
||||||
import groovy.transform.NullCheck
|
|
||||||
|
|
||||||
import java.util.function.Consumer
|
|
||||||
|
|
||||||
@NullCheck
|
|
||||||
@EqualsAndHashCode(includeFields = true)
|
|
||||||
final class EmbeddableTextsCollection {
|
|
||||||
|
|
||||||
@Delegate
|
|
||||||
private final Collection<EmbeddableText> embeddableTexts = []
|
|
||||||
|
|
||||||
EmbeddableTextsCollection(Collection<Text> texts, Consumer<Collection<Diagnostic>> diagnosticsConsumer) {
|
|
||||||
texts.each {
|
|
||||||
this << new EmbeddableText(it, diagnosticsConsumer)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
String toString() {
|
|
||||||
"EmbeddableTextsCollection(embeddableTexts: ${ this.embeddableTexts })"
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,84 +0,0 @@
|
|||||||
package com.jessebrault.ssg.dsl
|
|
||||||
|
|
||||||
import com.jessebrault.ssg.model.Model
|
|
||||||
import groovy.transform.EqualsAndHashCode
|
|
||||||
import groovy.transform.NullCheck
|
|
||||||
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.Predicate
|
|
||||||
|
|
||||||
@NullCheck
|
|
||||||
@EqualsAndHashCode(includeFields = true)
|
|
||||||
final class ModelCollection<T> {
|
|
||||||
|
|
||||||
private static final Logger logger = LoggerFactory.getLogger(ModelCollection)
|
|
||||||
private static final Marker enter = MarkerFactory.getMarker('ENTER')
|
|
||||||
private static final Marker exit = MarkerFactory.getMarker('EXIT')
|
|
||||||
|
|
||||||
@Delegate
|
|
||||||
private final Collection<Model<T>> models = []
|
|
||||||
|
|
||||||
ModelCollection(Collection<Model<T>> models) {
|
|
||||||
this.models.addAll(models)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
Model<T> getByName(String name) {
|
|
||||||
this.models.find { it.name == name }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
<E extends T> Model<E> getByNameAndType(String name, Class<E> type) {
|
|
||||||
this.models.find { it.name == name && type.isAssignableFrom(it.get().class) } as Model<E>
|
|
||||||
}
|
|
||||||
|
|
||||||
Optional<Model<T>> findByName(String name) {
|
|
||||||
Optional.ofNullable(this.getByName(name))
|
|
||||||
}
|
|
||||||
|
|
||||||
def <E extends T> Optional<Model<E>> findByNameAndType(String name, Class<E> type) {
|
|
||||||
Optional.ofNullable(this.getByNameAndType(name, type))
|
|
||||||
}
|
|
||||||
|
|
||||||
def <E> ModelCollection<E> findAllByType(Class<E> type) {
|
|
||||||
logger.trace(enter, 'type: {}', type)
|
|
||||||
def es = this.models.findResults {
|
|
||||||
def itType = it.get().class
|
|
||||||
def itResult = type.isAssignableFrom(itType)
|
|
||||||
logger.debug(
|
|
||||||
'it: {}, itType: {}, itType.classLoader: {}, itResult: {}',
|
|
||||||
it, itType, itType.classLoader, itResult
|
|
||||||
)
|
|
||||||
itResult ? it as Model<E> : null
|
|
||||||
}
|
|
||||||
def result = new ModelCollection<>(es)
|
|
||||||
logger.trace(exit, 'result: {}', result)
|
|
||||||
result
|
|
||||||
}
|
|
||||||
|
|
||||||
def <E extends T> Optional<Model<E>> findOne(Class<E> type, Predicate<E> filter) {
|
|
||||||
Optional.ofNullable(this.models.find {
|
|
||||||
def t = it.get()
|
|
||||||
if (type.isAssignableFrom(t.class)) {
|
|
||||||
filter.test(t as E) ? it : null
|
|
||||||
} else {
|
|
||||||
null
|
|
||||||
}
|
|
||||||
} as Model<E>)
|
|
||||||
}
|
|
||||||
|
|
||||||
def <E extends T> Optional<Model<E>> findOne(Class<E> type) {
|
|
||||||
this.findOne(type) { true }
|
|
||||||
}
|
|
||||||
|
|
||||||
Optional<Model<T>> findOne(Predicate<T> filter) {
|
|
||||||
Optional.ofNullable(this.models.find {
|
|
||||||
filter.test(it.get()) ? it : null
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,81 +0,0 @@
|
|||||||
package com.jessebrault.ssg.dsl
|
|
||||||
|
|
||||||
import com.jessebrault.ssg.dsl.tagbuilder.DynamicTagBuilder
|
|
||||||
import com.jessebrault.ssg.dsl.urlbuilder.PathBasedUrlBuilder
|
|
||||||
import com.jessebrault.ssg.render.RenderContext
|
|
||||||
import com.jessebrault.ssg.text.Text
|
|
||||||
import com.jessebrault.ssg.util.Diagnostic
|
|
||||||
import groovy.transform.NullCheck
|
|
||||||
import org.slf4j.LoggerFactory
|
|
||||||
|
|
||||||
import java.util.function.Consumer
|
|
||||||
|
|
||||||
final class StandardDslMap {
|
|
||||||
|
|
||||||
@NullCheck(includeGenerated = true)
|
|
||||||
static final class Builder {
|
|
||||||
|
|
||||||
private final Map<String, Object> custom = [:]
|
|
||||||
|
|
||||||
Consumer<Collection<Diagnostic>> diagnosticsConsumer = { }
|
|
||||||
String loggerName = ''
|
|
||||||
Text text = null
|
|
||||||
|
|
||||||
void putCustom(String key, Object value) {
|
|
||||||
this.custom.put(key, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
void putAllCustom(Map<String, Object> m) {
|
|
||||||
this.custom.putAll(m)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
static Map<String, Object> get(
|
|
||||||
RenderContext context,
|
|
||||||
Consumer<Builder> builderConsumer
|
|
||||||
) {
|
|
||||||
def b = new Builder()
|
|
||||||
builderConsumer.accept(b)
|
|
||||||
|
|
||||||
[:].tap {
|
|
||||||
// standard variables
|
|
||||||
it.globals = context.globals
|
|
||||||
it.logger = LoggerFactory.getLogger(b.loggerName)
|
|
||||||
it.models = new ModelCollection<Object>(context.models)
|
|
||||||
it.parts = new EmbeddablePartsMap(
|
|
||||||
context,
|
|
||||||
b.diagnosticsConsumer,
|
|
||||||
b.text
|
|
||||||
)
|
|
||||||
it.siteSpec = context.siteSpec
|
|
||||||
it.sourcePath = context.sourcePath
|
|
||||||
it.tagBuilder = new DynamicTagBuilder()
|
|
||||||
it.targetPath = context.targetPath
|
|
||||||
it.tasks = new TaskCollection(context.tasks)
|
|
||||||
it.text = b.text ? new EmbeddableText(
|
|
||||||
b.text,
|
|
||||||
b.diagnosticsConsumer
|
|
||||||
) : null
|
|
||||||
it.texts = new EmbeddableTextsCollection(
|
|
||||||
context.texts,
|
|
||||||
b.diagnosticsConsumer
|
|
||||||
)
|
|
||||||
it.urlBuilder = new PathBasedUrlBuilder(
|
|
||||||
context.targetPath,
|
|
||||||
context.siteSpec.baseUrl
|
|
||||||
)
|
|
||||||
|
|
||||||
// task types
|
|
||||||
it.Task = com.jessebrault.ssg.task.Task
|
|
||||||
it.HtmlTask = com.jessebrault.ssg.html.HtmlTask
|
|
||||||
it.ModelToHtmlTask = com.jessebrault.ssg.html.ModelToHtmlTask
|
|
||||||
it.PageToHtmlTask = com.jessebrault.ssg.html.PageToHtmlTask
|
|
||||||
it.TextToHtmlTask = com.jessebrault.ssg.html.TextToHtmlTask
|
|
||||||
|
|
||||||
// custom
|
|
||||||
it.putAll(b.custom)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,21 +0,0 @@
|
|||||||
package com.jessebrault.ssg.dsl
|
|
||||||
|
|
||||||
import com.jessebrault.ssg.task.Task
|
|
||||||
import groovy.transform.EqualsAndHashCode
|
|
||||||
|
|
||||||
@EqualsAndHashCode(includeFields = true)
|
|
||||||
final class TaskCollection {
|
|
||||||
|
|
||||||
@Delegate
|
|
||||||
private final Collection<Task> tasks
|
|
||||||
|
|
||||||
TaskCollection(Collection<? extends Task> src = []) {
|
|
||||||
this.tasks = []
|
|
||||||
this.tasks.addAll(src)
|
|
||||||
}
|
|
||||||
|
|
||||||
def <T extends Task> Collection<T> byType(Class<T> taskClass) {
|
|
||||||
this.tasks.findAll { taskClass.isAssignableFrom(it.class) } as Collection<T>
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,77 +0,0 @@
|
|||||||
package com.jessebrault.ssg.dsl.tagbuilder
|
|
||||||
|
|
||||||
import org.codehaus.groovy.runtime.InvokerHelper
|
|
||||||
|
|
||||||
final class DynamicTagBuilder implements TagBuilder {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
String create(String name) {
|
|
||||||
"<$name />"
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
String create(String name, Map<String, Object> attributes) {
|
|
||||||
def formattedAttributes = attributes.collect {
|
|
||||||
if (it.value instanceof String) {
|
|
||||||
it.key + '="' + it.value + '"'
|
|
||||||
} else if (it.value instanceof Integer) {
|
|
||||||
it.key + '=' + it.value
|
|
||||||
} else if (it.value instanceof Boolean && it.value == true) {
|
|
||||||
it.key
|
|
||||||
} else {
|
|
||||||
it.key + '="' + it.value.toString() + '"'
|
|
||||||
}
|
|
||||||
}.join(' ')
|
|
||||||
"<$name $formattedAttributes />"
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
String create(String name, String body) {
|
|
||||||
"<$name>$body</$name>"
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
String create(String name, Map<String, Object> attributes, String body) {
|
|
||||||
def formattedAttributes = attributes.collect {
|
|
||||||
if (it.value instanceof String) {
|
|
||||||
it.key + '="' + it.value + '"'
|
|
||||||
} else if (it.value instanceof Integer) {
|
|
||||||
it.key + '=' + it.value
|
|
||||||
} else if (it.value instanceof Boolean && it.value == true) {
|
|
||||||
it.key
|
|
||||||
} else {
|
|
||||||
it.key + '="' + it.value.toString() + '"'
|
|
||||||
}
|
|
||||||
}.join(' ')
|
|
||||||
"<$name $formattedAttributes>$body</$name>"
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
Object invokeMethod(String name, Object args) {
|
|
||||||
def argsList = InvokerHelper.asList(args)
|
|
||||||
return switch (argsList.size()) {
|
|
||||||
case 0 -> this.create(name)
|
|
||||||
case 1 -> {
|
|
||||||
def arg0 = argsList[0]
|
|
||||||
if (arg0 instanceof Map) {
|
|
||||||
this.create(name, arg0)
|
|
||||||
} else if (arg0 instanceof String) {
|
|
||||||
this.create(name, arg0)
|
|
||||||
} else {
|
|
||||||
throw new MissingMethodException(name, this.class, args, false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case 2 -> {
|
|
||||||
def arg0 = argsList[0]
|
|
||||||
def arg1 = argsList[1]
|
|
||||||
if (arg0 instanceof Map && arg1 instanceof String) {
|
|
||||||
this.create(name, arg0, arg1)
|
|
||||||
} else {
|
|
||||||
throw new MissingMethodException(name, this.class, args, false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
default -> throw new MissingMethodException(name, this.class, args, false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,8 +0,0 @@
|
|||||||
package com.jessebrault.ssg.dsl.tagbuilder
|
|
||||||
|
|
||||||
interface TagBuilder {
|
|
||||||
String create(String name)
|
|
||||||
String create(String name, Map<String, Object> attributes)
|
|
||||||
String create(String name, String body)
|
|
||||||
String create(String name, Map<String, Object> attributes, String body)
|
|
||||||
}
|
|
@ -1,59 +0,0 @@
|
|||||||
package com.jessebrault.ssg.html
|
|
||||||
|
|
||||||
import com.jessebrault.ssg.task.AbstractTask
|
|
||||||
import com.jessebrault.ssg.task.Task
|
|
||||||
import com.jessebrault.ssg.task.TaskInput
|
|
||||||
import com.jessebrault.ssg.util.Diagnostic
|
|
||||||
import com.jessebrault.ssg.util.Result
|
|
||||||
import groovy.transform.EqualsAndHashCode
|
|
||||||
import groovy.transform.NullCheck
|
|
||||||
import org.jsoup.Jsoup
|
|
||||||
|
|
||||||
@NullCheck
|
|
||||||
@EqualsAndHashCode
|
|
||||||
abstract class AbstractHtmlTask<I extends TaskInput> extends AbstractTask implements HtmlTask {
|
|
||||||
|
|
||||||
final String htmlPath
|
|
||||||
final I input
|
|
||||||
final HtmlOutput output
|
|
||||||
|
|
||||||
AbstractHtmlTask(
|
|
||||||
String name,
|
|
||||||
String htmlPath,
|
|
||||||
I input,
|
|
||||||
File buildDir
|
|
||||||
) {
|
|
||||||
super(name)
|
|
||||||
this.htmlPath = htmlPath
|
|
||||||
this.input = input
|
|
||||||
this.output = new SimpleHtmlOutput(
|
|
||||||
"htmlOutput:${ htmlPath }",
|
|
||||||
new File(buildDir, htmlPath),
|
|
||||||
htmlPath
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
protected abstract Result<String> transform(Collection<Task> allTasks)
|
|
||||||
|
|
||||||
@Override
|
|
||||||
final Collection<Diagnostic> execute(Collection<Task> allTasks) {
|
|
||||||
def transformResult = this.transform(allTasks)
|
|
||||||
if (transformResult.hasDiagnostics()) {
|
|
||||||
transformResult.diagnostics
|
|
||||||
} else {
|
|
||||||
def content = transformResult.get()
|
|
||||||
def document = Jsoup.parse(content)
|
|
||||||
document.outputSettings().indentAmount(4)
|
|
||||||
def formatted = document.toString()
|
|
||||||
this.output.file.createParentDirectories()
|
|
||||||
this.output.file.write(formatted)
|
|
||||||
[]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
String toString() {
|
|
||||||
"AbstractHtmlTask(path: ${ this.htmlPath }, super: ${ super.toString() })"
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,7 +0,0 @@
|
|||||||
package com.jessebrault.ssg.html
|
|
||||||
|
|
||||||
import com.jessebrault.ssg.task.FileOutput
|
|
||||||
|
|
||||||
interface HtmlOutput extends FileOutput {
|
|
||||||
String getHtmlPath()
|
|
||||||
}
|
|
@ -1,12 +0,0 @@
|
|||||||
package com.jessebrault.ssg.html
|
|
||||||
|
|
||||||
import com.jessebrault.ssg.task.Task
|
|
||||||
import com.jessebrault.ssg.task.TaskInput
|
|
||||||
|
|
||||||
interface HtmlTask extends Task {
|
|
||||||
@Deprecated
|
|
||||||
String getHtmlPath()
|
|
||||||
|
|
||||||
TaskInput getInput()
|
|
||||||
HtmlOutput getOutput()
|
|
||||||
}
|
|
@ -1,16 +0,0 @@
|
|||||||
package com.jessebrault.ssg.html
|
|
||||||
|
|
||||||
import com.jessebrault.ssg.model.Model
|
|
||||||
import com.jessebrault.ssg.template.Template
|
|
||||||
import groovy.transform.EqualsAndHashCode
|
|
||||||
import groovy.transform.NullCheck
|
|
||||||
import groovy.transform.TupleConstructor
|
|
||||||
|
|
||||||
@TupleConstructor(defaults = false)
|
|
||||||
@NullCheck(includeGenerated = true)
|
|
||||||
@EqualsAndHashCode
|
|
||||||
final class ModelToHtmlSpec<T> {
|
|
||||||
final Model<T> model
|
|
||||||
final Template template
|
|
||||||
final String path
|
|
||||||
}
|
|
@ -1,71 +0,0 @@
|
|||||||
package com.jessebrault.ssg.html
|
|
||||||
|
|
||||||
import com.jessebrault.ssg.SiteSpec
|
|
||||||
import com.jessebrault.ssg.model.Model
|
|
||||||
import com.jessebrault.ssg.model.ModelInput
|
|
||||||
import com.jessebrault.ssg.part.Part
|
|
||||||
import com.jessebrault.ssg.render.RenderContext
|
|
||||||
import com.jessebrault.ssg.task.Task
|
|
||||||
import com.jessebrault.ssg.task.TaskSpec
|
|
||||||
import com.jessebrault.ssg.template.Template
|
|
||||||
import com.jessebrault.ssg.text.Text
|
|
||||||
import com.jessebrault.ssg.util.Result
|
|
||||||
import groovy.transform.EqualsAndHashCode
|
|
||||||
import groovy.transform.NullCheck
|
|
||||||
|
|
||||||
@NullCheck
|
|
||||||
@EqualsAndHashCode
|
|
||||||
final class ModelToHtmlTask<T> extends AbstractHtmlTask<ModelInput<T>> {
|
|
||||||
|
|
||||||
private final SiteSpec siteSpec
|
|
||||||
private final Map<String, Object> globals
|
|
||||||
private final Model<T> model
|
|
||||||
private final Template template
|
|
||||||
private final Collection<Text> allTexts
|
|
||||||
private final Collection<Model<Object>> allModels
|
|
||||||
private final Collection<Part> allParts
|
|
||||||
|
|
||||||
ModelToHtmlTask(
|
|
||||||
String relativeHtmlPath,
|
|
||||||
TaskSpec taskSpec,
|
|
||||||
Model<T> model,
|
|
||||||
Template template,
|
|
||||||
Collection<Text> allTexts,
|
|
||||||
Collection<Model<Object>> allModels,
|
|
||||||
Collection<Part> allParts
|
|
||||||
) {
|
|
||||||
super(
|
|
||||||
"modelToHtml:${ relativeHtmlPath }",
|
|
||||||
relativeHtmlPath,
|
|
||||||
new ModelInput<>(model.name, model),
|
|
||||||
taskSpec.outputDir
|
|
||||||
)
|
|
||||||
this.siteSpec = taskSpec.siteSpec
|
|
||||||
this.globals = taskSpec.globals
|
|
||||||
this.model = model
|
|
||||||
this.template = template
|
|
||||||
this.allTexts = allTexts
|
|
||||||
this.allModels = allModels
|
|
||||||
this.allParts = allParts
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Result<String> transform(Collection<Task> allTasks) {
|
|
||||||
this.template.type.renderer.render(this.template, null, new RenderContext(
|
|
||||||
sourcePath: this.model.name,
|
|
||||||
targetPath: this.htmlPath,
|
|
||||||
tasks: allTasks,
|
|
||||||
texts: this.allTexts,
|
|
||||||
models: this.allModels,
|
|
||||||
parts: this.allParts,
|
|
||||||
siteSpec: this.siteSpec,
|
|
||||||
globals: this.globals
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
String toString() {
|
|
||||||
"ModelToHtml(${ this.model }, ${ this.template }, ${ this.allTexts }, ${ this.allParts }, ${ super.toString() })"
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,33 +0,0 @@
|
|||||||
package com.jessebrault.ssg.html
|
|
||||||
|
|
||||||
import com.jessebrault.ssg.provider.CollectionProvider
|
|
||||||
import com.jessebrault.ssg.provider.CollectionProviders
|
|
||||||
import com.jessebrault.ssg.task.AbstractRenderTaskFactory
|
|
||||||
import com.jessebrault.ssg.task.Task
|
|
||||||
import com.jessebrault.ssg.task.TaskSpec
|
|
||||||
import com.jessebrault.ssg.util.Result
|
|
||||||
|
|
||||||
final class ModelToHtmlTaskFactory<T> extends AbstractRenderTaskFactory {
|
|
||||||
|
|
||||||
CollectionProvider<ModelToHtmlSpec<T>> specsProvider = CollectionProviders.getEmpty()
|
|
||||||
|
|
||||||
@Override
|
|
||||||
Result<Collection<Task>> getTasks(TaskSpec taskSpec) {
|
|
||||||
def allTexts = this.allTextsProvider.provide()
|
|
||||||
def allModels = this.allModelsProvider.provide()
|
|
||||||
def allParts = this.allPartsProvider.provide()
|
|
||||||
|
|
||||||
Result.of(specsProvider.provide().collect {
|
|
||||||
new ModelToHtmlTask<>(
|
|
||||||
it.path,
|
|
||||||
taskSpec,
|
|
||||||
it.model,
|
|
||||||
it.template,
|
|
||||||
allTexts,
|
|
||||||
allModels,
|
|
||||||
allParts
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,17 +0,0 @@
|
|||||||
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
|
|
||||||
}
|
|
@ -1,34 +0,0 @@
|
|||||||
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 groovy.transform.NullCheck
|
|
||||||
|
|
||||||
import java.util.function.Function
|
|
||||||
|
|
||||||
@NullCheck
|
|
||||||
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() {}
|
|
||||||
|
|
||||||
}
|
|
@ -1,68 +0,0 @@
|
|||||||
package com.jessebrault.ssg.html
|
|
||||||
|
|
||||||
import com.jessebrault.ssg.SiteSpec
|
|
||||||
import com.jessebrault.ssg.model.Model
|
|
||||||
import com.jessebrault.ssg.page.Page
|
|
||||||
import com.jessebrault.ssg.page.PageInput
|
|
||||||
import com.jessebrault.ssg.part.Part
|
|
||||||
import com.jessebrault.ssg.render.RenderContext
|
|
||||||
import com.jessebrault.ssg.task.Task
|
|
||||||
import com.jessebrault.ssg.task.TaskSpec
|
|
||||||
import com.jessebrault.ssg.text.Text
|
|
||||||
import com.jessebrault.ssg.util.Result
|
|
||||||
import groovy.transform.EqualsAndHashCode
|
|
||||||
import groovy.transform.NullCheck
|
|
||||||
|
|
||||||
@NullCheck
|
|
||||||
@EqualsAndHashCode(includeFields = true, callSuper = true)
|
|
||||||
final class PageToHtmlTask extends AbstractHtmlTask<PageInput> {
|
|
||||||
|
|
||||||
private final SiteSpec siteSpec
|
|
||||||
private final Map<String, Object> globals
|
|
||||||
private final Page page
|
|
||||||
private final Collection<Text> allTexts
|
|
||||||
private final Collection<Model<Object>> allModels
|
|
||||||
private final Collection<Part> allParts
|
|
||||||
|
|
||||||
PageToHtmlTask(
|
|
||||||
String relativeHtmlPath,
|
|
||||||
TaskSpec taskSpec,
|
|
||||||
Page page,
|
|
||||||
Collection<Text> allTexts,
|
|
||||||
Collection<Model<Object>> allModels,
|
|
||||||
Collection<Part> allParts
|
|
||||||
) {
|
|
||||||
super(
|
|
||||||
"pageToHtml:${ relativeHtmlPath }",
|
|
||||||
relativeHtmlPath,
|
|
||||||
new PageInput(page.path, page),
|
|
||||||
taskSpec.outputDir
|
|
||||||
)
|
|
||||||
this.siteSpec = taskSpec.siteSpec
|
|
||||||
this.globals = taskSpec.globals
|
|
||||||
this.page = page
|
|
||||||
this.allTexts = allTexts
|
|
||||||
this.allModels = allModels
|
|
||||||
this.allParts = allParts
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Result<String> transform(Collection<Task> allTasks) {
|
|
||||||
this.page.type.renderer.render(this.page, new RenderContext(
|
|
||||||
this.page.path,
|
|
||||||
this.htmlPath,
|
|
||||||
allTasks,
|
|
||||||
this.allTexts,
|
|
||||||
this.allModels,
|
|
||||||
this.allParts,
|
|
||||||
this.siteSpec,
|
|
||||||
this.globals
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
String toString() {
|
|
||||||
"PageToHtml(${ this.page }, ${ this.allTexts }, ${ this.allParts }, ${ super.toString() })"
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,39 +0,0 @@
|
|||||||
package com.jessebrault.ssg.html
|
|
||||||
|
|
||||||
|
|
||||||
import com.jessebrault.ssg.provider.CollectionProvider
|
|
||||||
import com.jessebrault.ssg.provider.CollectionProviders
|
|
||||||
import com.jessebrault.ssg.task.AbstractRenderTaskFactory
|
|
||||||
import com.jessebrault.ssg.task.Task
|
|
||||||
import com.jessebrault.ssg.task.TaskSpec
|
|
||||||
import com.jessebrault.ssg.util.Result
|
|
||||||
|
|
||||||
import static java.util.Objects.requireNonNull
|
|
||||||
|
|
||||||
final class PageToHtmlTaskFactory extends AbstractRenderTaskFactory {
|
|
||||||
|
|
||||||
CollectionProvider<PageToHtmlSpec> specsProvider = CollectionProviders.getEmpty()
|
|
||||||
|
|
||||||
@Override
|
|
||||||
Result<Collection<Task>> getTasks(TaskSpec taskSpec) {
|
|
||||||
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.specsProvider.provide()
|
|
||||||
.collect {
|
|
||||||
new PageToHtmlTask(
|
|
||||||
it.toRelativeHtmlPath.apply(taskSpec),
|
|
||||||
taskSpec,
|
|
||||||
it.page,
|
|
||||||
allTexts,
|
|
||||||
allModels,
|
|
||||||
allParts
|
|
||||||
)
|
|
||||||
}
|
|
||||||
Result.of(tasks)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,15 +0,0 @@
|
|||||||
package com.jessebrault.ssg.html
|
|
||||||
|
|
||||||
import groovy.transform.EqualsAndHashCode
|
|
||||||
import groovy.transform.NullCheck
|
|
||||||
import groovy.transform.PackageScope
|
|
||||||
import groovy.transform.TupleConstructor
|
|
||||||
|
|
||||||
@TupleConstructor(defaults = false)
|
|
||||||
@NullCheck(includeGenerated = true)
|
|
||||||
@EqualsAndHashCode
|
|
||||||
final class SimpleHtmlOutput implements HtmlOutput {
|
|
||||||
final String name
|
|
||||||
final File file
|
|
||||||
final String htmlPath
|
|
||||||
}
|
|
@ -1,19 +0,0 @@
|
|||||||
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 Function<TaskSpec, String> toRelativeHtmlPath
|
|
||||||
}
|
|
@ -1,60 +0,0 @@
|
|||||||
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 = templatesProvider.provide()
|
|
||||||
textsProvider.provide().findResults {
|
|
||||||
def frontMatterResult = it.type.frontMatterGetter.get(it)
|
|
||||||
if (frontMatterResult.hasDiagnostics()) {
|
|
||||||
return Result.ofDiagnostics(frontMatterResult.diagnostics) as Result<TextToHtmlSpec>
|
|
||||||
}
|
|
||||||
def templateValue = frontMatterResult.get().get('template')
|
|
||||||
if (templateValue) {
|
|
||||||
def template = templates.find { it.path == templateValue }
|
|
||||||
return Result.of(new TextToHtmlSpec(
|
|
||||||
it,
|
|
||||||
template,
|
|
||||||
toRelativeHtmlPath.apply(it)
|
|
||||||
))
|
|
||||||
} else {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private TextToHtmlSpecProviders() {}
|
|
||||||
|
|
||||||
}
|
|
@ -1,71 +0,0 @@
|
|||||||
package com.jessebrault.ssg.html
|
|
||||||
|
|
||||||
import com.jessebrault.ssg.SiteSpec
|
|
||||||
import com.jessebrault.ssg.model.Model
|
|
||||||
import com.jessebrault.ssg.part.Part
|
|
||||||
import com.jessebrault.ssg.render.RenderContext
|
|
||||||
import com.jessebrault.ssg.task.Task
|
|
||||||
import com.jessebrault.ssg.task.TaskSpec
|
|
||||||
import com.jessebrault.ssg.template.Template
|
|
||||||
import com.jessebrault.ssg.text.Text
|
|
||||||
import com.jessebrault.ssg.text.TextInput
|
|
||||||
import com.jessebrault.ssg.util.Result
|
|
||||||
import groovy.transform.EqualsAndHashCode
|
|
||||||
import groovy.transform.NullCheck
|
|
||||||
|
|
||||||
@NullCheck
|
|
||||||
@EqualsAndHashCode(includeFields = true, callSuper = true)
|
|
||||||
final class TextToHtmlTask extends AbstractHtmlTask<TextInput> {
|
|
||||||
|
|
||||||
private final SiteSpec siteSpec
|
|
||||||
private final Map<String, Object> globals
|
|
||||||
private final Text text
|
|
||||||
private final Template template
|
|
||||||
private final Collection<Text> allTexts
|
|
||||||
private final Collection<Model<Object>> allModels
|
|
||||||
private final Collection<Part> allParts
|
|
||||||
|
|
||||||
TextToHtmlTask(
|
|
||||||
String relativeHtmlPath,
|
|
||||||
TaskSpec taskSpec,
|
|
||||||
Text text,
|
|
||||||
Template template,
|
|
||||||
Collection<Text> allTexts,
|
|
||||||
Collection<Model<Object>> allModels,
|
|
||||||
Collection<Part> allParts
|
|
||||||
) {
|
|
||||||
super(
|
|
||||||
"textToHtml:${ relativeHtmlPath }",
|
|
||||||
relativeHtmlPath,
|
|
||||||
new TextInput(text.path, text),
|
|
||||||
taskSpec.outputDir
|
|
||||||
)
|
|
||||||
this.siteSpec = taskSpec.siteSpec
|
|
||||||
this.globals = taskSpec.globals
|
|
||||||
this.text = text
|
|
||||||
this.template = template
|
|
||||||
this.allTexts = allTexts
|
|
||||||
this.allModels = allModels
|
|
||||||
this.allParts = allParts
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Result<String> transform(Collection<Task> allTasks) {
|
|
||||||
this.template.type.renderer.render(this.template, this.text, new RenderContext(
|
|
||||||
this.text.path,
|
|
||||||
this.htmlPath,
|
|
||||||
allTasks,
|
|
||||||
this.allTexts,
|
|
||||||
this.allModels,
|
|
||||||
this.allParts,
|
|
||||||
this.siteSpec,
|
|
||||||
this.globals
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
String toString() {
|
|
||||||
"TextToHtml(${ this.text }, ${ this.template }, ${ this.allTexts }, ${ this.allParts }, ${ super.toString() })"
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,47 +0,0 @@
|
|||||||
package com.jessebrault.ssg.html
|
|
||||||
|
|
||||||
import com.jessebrault.ssg.provider.CollectionProvider
|
|
||||||
import com.jessebrault.ssg.provider.CollectionProviders
|
|
||||||
import com.jessebrault.ssg.task.AbstractRenderTaskFactory
|
|
||||||
import com.jessebrault.ssg.task.Task
|
|
||||||
import com.jessebrault.ssg.task.TaskSpec
|
|
||||||
import com.jessebrault.ssg.util.Diagnostic
|
|
||||||
import com.jessebrault.ssg.util.Result
|
|
||||||
|
|
||||||
import static java.util.Objects.requireNonNull
|
|
||||||
|
|
||||||
final class TextToHtmlTaskFactory extends AbstractRenderTaskFactory {
|
|
||||||
|
|
||||||
CollectionProvider<Result<TextToHtmlSpec>> specsProvider = CollectionProviders.getEmpty()
|
|
||||||
|
|
||||||
@Override
|
|
||||||
Result<Collection<Task>> getTasks(TaskSpec taskSpec) {
|
|
||||||
super.checkProviders()
|
|
||||||
requireNonNull(this.specsProvider)
|
|
||||||
|
|
||||||
def allTexts = this.allTextsProvider.provide()
|
|
||||||
def allModels = this.allModelsProvider.provide()
|
|
||||||
def allParts = this.allPartsProvider.provide()
|
|
||||||
|
|
||||||
Collection<Diagnostic> diagnostics = []
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -2,5 +2,6 @@ package com.jessebrault.ssg.model
|
|||||||
|
|
||||||
interface Model<T> {
|
interface Model<T> {
|
||||||
String getName()
|
String getName()
|
||||||
|
Class<T> getType()
|
||||||
T get()
|
T get()
|
||||||
}
|
}
|
||||||
|
@ -1,14 +0,0 @@
|
|||||||
package com.jessebrault.ssg.model
|
|
||||||
|
|
||||||
import com.jessebrault.ssg.task.TaskInput
|
|
||||||
import groovy.transform.EqualsAndHashCode
|
|
||||||
import groovy.transform.NullCheck
|
|
||||||
import groovy.transform.TupleConstructor
|
|
||||||
|
|
||||||
@TupleConstructor(defaults = false)
|
|
||||||
@NullCheck(includeGenerated = true)
|
|
||||||
@EqualsAndHashCode
|
|
||||||
final class ModelInput<T> implements TaskInput {
|
|
||||||
final String name
|
|
||||||
final Model<T> model
|
|
||||||
}
|
|
@ -7,20 +7,21 @@ import java.util.function.Supplier
|
|||||||
|
|
||||||
final class Models {
|
final class Models {
|
||||||
|
|
||||||
|
@SuppressWarnings('GroovyAssignabilityCheck')
|
||||||
static <T> Model<T> of(String name, T t) {
|
static <T> Model<T> of(String name, T t) {
|
||||||
new SimpleModel<>(name, t)
|
new SimpleModel<>(name, t.class, t)
|
||||||
}
|
}
|
||||||
|
|
||||||
static <T> Model<T> ofSupplier(String name, Supplier<? extends T> tClosure) {
|
static <T> Model<T> ofSupplier(String name, Class<T> type, Supplier<? extends T> tClosure) {
|
||||||
new SupplierBasedModel<>(name, tClosure)
|
new SupplierBasedModel<>(name, type, tClosure)
|
||||||
}
|
}
|
||||||
|
|
||||||
static <T> Model<T> ofProvider(String name, Provider<? extends T> modelProvider) {
|
static <T> Model<T> ofProvider(String name, Provider<? extends T> modelProvider) {
|
||||||
new ProviderModel<T>(name, modelProvider)
|
new ProviderModel<T>(name, modelProvider.type, modelProvider)
|
||||||
}
|
}
|
||||||
|
|
||||||
static <T> Model<T> ofNamedProvider(NamedProvider<? extends T> namedModelProvider) {
|
static <T> Model<T> ofNamedProvider(NamedProvider<? extends T> namedModelProvider) {
|
||||||
new ProviderModel<T>(namedModelProvider.name, namedModelProvider)
|
new ProviderModel<T>(namedModelProvider.name, namedModelProvider.type, namedModelProvider)
|
||||||
}
|
}
|
||||||
|
|
||||||
private Models() {}
|
private Models() {}
|
||||||
|
@ -13,6 +13,7 @@ import groowt.util.fp.provider.Provider
|
|||||||
class ProviderModel<T> implements Model<T> {
|
class ProviderModel<T> implements Model<T> {
|
||||||
|
|
||||||
final String name
|
final String name
|
||||||
|
final Class<T> type
|
||||||
private final Provider<T> modelProvider
|
private final Provider<T> modelProvider
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -22,6 +23,6 @@ class ProviderModel<T> implements Model<T> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
String toString() {
|
String toString() {
|
||||||
"ProviderModel($this.name)"
|
"ProviderModel(name: $name, type: $type)"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,7 @@ import groovy.transform.TupleConstructor
|
|||||||
final class SimpleModel<T> implements Model<T> {
|
final class SimpleModel<T> implements Model<T> {
|
||||||
|
|
||||||
final String name
|
final String name
|
||||||
|
final Class<T> type
|
||||||
private final T t
|
private final T t
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -21,7 +22,7 @@ final class SimpleModel<T> implements Model<T> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
String toString() {
|
String toString() {
|
||||||
"SimpleModel(${ this.t })"
|
"SimpleModel(name: $name, type: $type.name)"
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -8,21 +8,23 @@ import groovy.transform.TupleConstructor
|
|||||||
import java.util.function.Supplier
|
import java.util.function.Supplier
|
||||||
|
|
||||||
@PackageScope
|
@PackageScope
|
||||||
|
@TupleConstructor(includeFields = true, defaults = false)
|
||||||
@NullCheck(includeGenerated = true)
|
@NullCheck(includeGenerated = true)
|
||||||
@EqualsAndHashCode(includeFields = true)
|
@EqualsAndHashCode(includeFields = true)
|
||||||
final class SupplierBasedModel<T> implements Model<T> {
|
final class SupplierBasedModel<T> implements Model<T> {
|
||||||
|
|
||||||
final String name
|
final String name
|
||||||
private final Supplier<? extends T> supplier
|
final Class<T> type
|
||||||
|
|
||||||
SupplierBasedModel(String name, Supplier<? extends T> supplier) {
|
private final Supplier<? extends T> supplier
|
||||||
this.name = name
|
|
||||||
this.supplier = supplier
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
T get() {
|
T get() {
|
||||||
this.supplier.get()
|
this.supplier.get()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
String toString() {
|
||||||
|
"SupplierBasedModel(name: $name, type: $type.name)"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,23 @@
|
|||||||
|
package com.jessebrault.ssg.page
|
||||||
|
|
||||||
|
import com.jessebrault.ssg.view.PageView
|
||||||
|
import groovy.transform.EqualsAndHashCode
|
||||||
|
import groovy.transform.NullCheck
|
||||||
|
import groovy.transform.TupleConstructor
|
||||||
|
|
||||||
|
@TupleConstructor(defaults = false)
|
||||||
|
@NullCheck
|
||||||
|
@EqualsAndHashCode
|
||||||
|
class DefaultPage implements Page {
|
||||||
|
|
||||||
|
final String name
|
||||||
|
final String path
|
||||||
|
final String fileExtension
|
||||||
|
final Class<? extends PageView> viewType
|
||||||
|
|
||||||
|
@Override
|
||||||
|
String toString() {
|
||||||
|
"SimplePage(name: $name, path: $path, fileExtension: $fileExtension)"
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,41 +0,0 @@
|
|||||||
package com.jessebrault.ssg.page
|
|
||||||
|
|
||||||
import com.jessebrault.ssg.render.RenderContext
|
|
||||||
import com.jessebrault.ssg.render.StandardGspRenderer
|
|
||||||
import com.jessebrault.ssg.util.Result
|
|
||||||
import groovy.transform.EqualsAndHashCode
|
|
||||||
import groovy.transform.NullCheck
|
|
||||||
|
|
||||||
@NullCheck
|
|
||||||
@EqualsAndHashCode
|
|
||||||
final class GspPageRenderer implements PageRenderer {
|
|
||||||
|
|
||||||
private final StandardGspRenderer gspRenderer
|
|
||||||
|
|
||||||
GspPageRenderer(ClassLoader parentClassLoader) {
|
|
||||||
this.gspRenderer = new StandardGspRenderer(parentClassLoader)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
Result<String> render(
|
|
||||||
Page specialPage,
|
|
||||||
RenderContext context
|
|
||||||
) {
|
|
||||||
def diagnostics = []
|
|
||||||
def result = this.gspRenderer.render(specialPage.text, context) {
|
|
||||||
it.diagnosticsConsumer = diagnostics.&addAll
|
|
||||||
it.loggerName = "GspSpecialPage(${ specialPage.path })"
|
|
||||||
}
|
|
||||||
if (result.hasDiagnostics()) {
|
|
||||||
Result.ofDiagnostics(diagnostics + result.diagnostics)
|
|
||||||
} else {
|
|
||||||
Result.of(diagnostics, result.get())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
String toString() {
|
|
||||||
"GspSpecialPageRenderer()"
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,21 +1,10 @@
|
|||||||
package com.jessebrault.ssg.page
|
package com.jessebrault.ssg.page
|
||||||
|
|
||||||
import groovy.transform.EqualsAndHashCode
|
import com.jessebrault.ssg.view.PageView
|
||||||
import groovy.transform.NullCheck
|
|
||||||
import groovy.transform.TupleConstructor
|
|
||||||
|
|
||||||
@TupleConstructor(defaults = false)
|
|
||||||
@NullCheck(includeGenerated = true)
|
|
||||||
@EqualsAndHashCode
|
|
||||||
final class Page {
|
|
||||||
|
|
||||||
final String path
|
|
||||||
final PageType type
|
|
||||||
final String text
|
|
||||||
|
|
||||||
@Override
|
|
||||||
String toString() {
|
|
||||||
"Page(path: ${ this.path }, type: ${ this.type })"
|
|
||||||
}
|
|
||||||
|
|
||||||
|
interface Page {
|
||||||
|
String getName()
|
||||||
|
String getPath()
|
||||||
|
String getFileExtension()
|
||||||
|
Class<? extends PageView> getViewType()
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,5 @@
|
|||||||
|
package com.jessebrault.ssg.page
|
||||||
|
|
||||||
|
interface PageFactory {
|
||||||
|
Collection<Page> create()
|
||||||
|
}
|
@ -1,14 +0,0 @@
|
|||||||
package com.jessebrault.ssg.page
|
|
||||||
|
|
||||||
import com.jessebrault.ssg.task.TaskInput
|
|
||||||
import groovy.transform.EqualsAndHashCode
|
|
||||||
import groovy.transform.NullCheck
|
|
||||||
import groovy.transform.TupleConstructor
|
|
||||||
|
|
||||||
@TupleConstructor(defaults = false)
|
|
||||||
@NullCheck(includeGenerated = true)
|
|
||||||
@EqualsAndHashCode
|
|
||||||
final class PageInput implements TaskInput {
|
|
||||||
final String name
|
|
||||||
final Page page
|
|
||||||
}
|
|
@ -1,13 +0,0 @@
|
|||||||
package com.jessebrault.ssg.page
|
|
||||||
|
|
||||||
import com.jessebrault.ssg.render.RenderContext
|
|
||||||
import com.jessebrault.ssg.util.Result
|
|
||||||
|
|
||||||
interface PageRenderer {
|
|
||||||
|
|
||||||
Result<String> render(
|
|
||||||
Page specialPage,
|
|
||||||
RenderContext context
|
|
||||||
)
|
|
||||||
|
|
||||||
}
|
|
14
api/src/main/groovy/com/jessebrault/ssg/page/PageSpec.groovy
Normal file
14
api/src/main/groovy/com/jessebrault/ssg/page/PageSpec.groovy
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
package com.jessebrault.ssg.page
|
||||||
|
|
||||||
|
import java.lang.annotation.ElementType
|
||||||
|
import java.lang.annotation.Retention
|
||||||
|
import java.lang.annotation.RetentionPolicy
|
||||||
|
import java.lang.annotation.Target
|
||||||
|
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Target(ElementType.TYPE)
|
||||||
|
@interface PageSpec {
|
||||||
|
String name()
|
||||||
|
String path()
|
||||||
|
String fileExtension() default '.html'
|
||||||
|
}
|
@ -1,20 +0,0 @@
|
|||||||
package com.jessebrault.ssg.page
|
|
||||||
|
|
||||||
import groovy.transform.EqualsAndHashCode
|
|
||||||
import groovy.transform.NullCheck
|
|
||||||
import groovy.transform.TupleConstructor
|
|
||||||
|
|
||||||
@TupleConstructor(defaults = false)
|
|
||||||
@NullCheck(includeGenerated = true)
|
|
||||||
@EqualsAndHashCode
|
|
||||||
final class PageType {
|
|
||||||
|
|
||||||
Collection<String> ids
|
|
||||||
PageRenderer renderer
|
|
||||||
|
|
||||||
@Override
|
|
||||||
String toString() {
|
|
||||||
"PageType(ids: ${ this.ids }, renderer: ${ this.renderer })"
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,14 +0,0 @@
|
|||||||
package com.jessebrault.ssg.page
|
|
||||||
|
|
||||||
import groovy.transform.NullCheck
|
|
||||||
|
|
||||||
@NullCheck
|
|
||||||
final class PageTypes {
|
|
||||||
|
|
||||||
static PageType getGsp(Collection<String> extensions, ClassLoader parentClassLoader) {
|
|
||||||
new PageType(extensions, new GspPageRenderer(parentClassLoader))
|
|
||||||
}
|
|
||||||
|
|
||||||
private PageTypes() {}
|
|
||||||
|
|
||||||
}
|
|
@ -1,50 +0,0 @@
|
|||||||
package com.jessebrault.ssg.page
|
|
||||||
|
|
||||||
import com.jessebrault.ssg.provider.CollectionProvider
|
|
||||||
import com.jessebrault.ssg.provider.CollectionProviders
|
|
||||||
import com.jessebrault.ssg.util.ExtensionUtil
|
|
||||||
import groovy.transform.NullCheck
|
|
||||||
import org.slf4j.Logger
|
|
||||||
import org.slf4j.LoggerFactory
|
|
||||||
|
|
||||||
import java.util.function.BiPredicate
|
|
||||||
import java.util.function.Predicate
|
|
||||||
|
|
||||||
@NullCheck
|
|
||||||
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 ->
|
|
||||||
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 {
|
|
||||||
logger.debug('there is no extension for file {}; skipping', file)
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private PagesProviders() {}
|
|
||||||
|
|
||||||
}
|
|
@ -1,52 +0,0 @@
|
|||||||
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.Result
|
|
||||||
import groovy.transform.EqualsAndHashCode
|
|
||||||
import org.jetbrains.annotations.Nullable
|
|
||||||
|
|
||||||
import static java.util.Objects.requireNonNull
|
|
||||||
|
|
||||||
@EqualsAndHashCode
|
|
||||||
final class GspPartRenderer implements PartRenderer {
|
|
||||||
|
|
||||||
private final StandardGspRenderer gspRenderer
|
|
||||||
|
|
||||||
GspPartRenderer(ClassLoader parentClassLoader) {
|
|
||||||
this.gspRenderer = new StandardGspRenderer(parentClassLoader)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
Result<String> render(
|
|
||||||
Part part,
|
|
||||||
Map<String, Object> binding,
|
|
||||||
RenderContext context,
|
|
||||||
@Nullable Text text
|
|
||||||
) {
|
|
||||||
requireNonNull(part)
|
|
||||||
requireNonNull(binding)
|
|
||||||
requireNonNull(context)
|
|
||||||
def diagnostics = []
|
|
||||||
def result = this.gspRenderer.render(part.text, context) {
|
|
||||||
it.putCustom('binding', binding)
|
|
||||||
it.diagnosticsConsumer = diagnostics.&addAll
|
|
||||||
it.loggerName = "GspPart(${ part.path })"
|
|
||||||
if (text) {
|
|
||||||
it.text = text
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (result.hasDiagnostics()) {
|
|
||||||
Result.ofDiagnostics(diagnostics + result.diagnostics)
|
|
||||||
} else {
|
|
||||||
Result.of(diagnostics, result.get())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
String toString() {
|
|
||||||
"GspPartRenderer()"
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,21 +0,0 @@
|
|||||||
package com.jessebrault.ssg.part
|
|
||||||
|
|
||||||
import groovy.transform.EqualsAndHashCode
|
|
||||||
import groovy.transform.NullCheck
|
|
||||||
import groovy.transform.TupleConstructor
|
|
||||||
|
|
||||||
@TupleConstructor(defaults = false)
|
|
||||||
@NullCheck(includeGenerated = true)
|
|
||||||
@EqualsAndHashCode
|
|
||||||
final class Part {
|
|
||||||
|
|
||||||
String path
|
|
||||||
PartType type
|
|
||||||
String text
|
|
||||||
|
|
||||||
@Override
|
|
||||||
String toString() {
|
|
||||||
"Part(path: ${ this.path }, type: ${ this.type })"
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,17 +0,0 @@
|
|||||||
package com.jessebrault.ssg.part
|
|
||||||
|
|
||||||
import com.jessebrault.ssg.render.RenderContext
|
|
||||||
import com.jessebrault.ssg.util.Result
|
|
||||||
import com.jessebrault.ssg.text.Text
|
|
||||||
import org.jetbrains.annotations.Nullable
|
|
||||||
|
|
||||||
interface PartRenderer {
|
|
||||||
|
|
||||||
Result<String> render(
|
|
||||||
Part part,
|
|
||||||
Map<String, Object> binding,
|
|
||||||
RenderContext context,
|
|
||||||
@Nullable Text text
|
|
||||||
)
|
|
||||||
|
|
||||||
}
|
|
@ -1,20 +0,0 @@
|
|||||||
package com.jessebrault.ssg.part
|
|
||||||
|
|
||||||
import groovy.transform.EqualsAndHashCode
|
|
||||||
import groovy.transform.NullCheck
|
|
||||||
import groovy.transform.TupleConstructor
|
|
||||||
|
|
||||||
@TupleConstructor(defaults = false)
|
|
||||||
@NullCheck(includeGenerated = true)
|
|
||||||
@EqualsAndHashCode
|
|
||||||
final class PartType {
|
|
||||||
|
|
||||||
Collection<String> ids
|
|
||||||
PartRenderer renderer
|
|
||||||
|
|
||||||
@Override
|
|
||||||
String toString() {
|
|
||||||
"PartType(ids: ${ this.ids }, renderer: ${ this.renderer })"
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,14 +0,0 @@
|
|||||||
package com.jessebrault.ssg.part
|
|
||||||
|
|
||||||
import groovy.transform.NullCheck
|
|
||||||
|
|
||||||
@NullCheck
|
|
||||||
final class PartTypes {
|
|
||||||
|
|
||||||
static PartType getGsp(Collection<String> extensions, ClassLoader parentClassLoader) {
|
|
||||||
new PartType(extensions, new GspPartRenderer(parentClassLoader))
|
|
||||||
}
|
|
||||||
|
|
||||||
private PartTypes() {}
|
|
||||||
|
|
||||||
}
|
|
@ -1,33 +0,0 @@
|
|||||||
package com.jessebrault.ssg.part
|
|
||||||
|
|
||||||
import com.jessebrault.ssg.provider.CollectionProvider
|
|
||||||
import com.jessebrault.ssg.provider.CollectionProviders
|
|
||||||
import com.jessebrault.ssg.util.ExtensionUtil
|
|
||||||
import org.slf4j.Logger
|
|
||||||
import org.slf4j.LoggerFactory
|
|
||||||
|
|
||||||
final class PartsProviders {
|
|
||||||
|
|
||||||
private static final Logger logger = LoggerFactory.getLogger(PartsProviders)
|
|
||||||
|
|
||||||
static CollectionProvider<Part> from(File partsDir, Collection<PartType> partTypes) {
|
|
||||||
CollectionProviders.fromDirectory(partsDir) { file, relativePath ->
|
|
||||||
def extension = ExtensionUtil.getExtension(relativePath)
|
|
||||||
if (extension) {
|
|
||||||
def partType = partTypes.find { it.ids.contains(extension) }
|
|
||||||
if (!partType) {
|
|
||||||
logger.debug('there is no PartType for file {}; skipping', file)
|
|
||||||
return null
|
|
||||||
} else {
|
|
||||||
return new Part(relativePath, partType, file.getText())
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
logger.debug('there is no extension for file {}; skipping', file)
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private PartsProviders() {}
|
|
||||||
|
|
||||||
}
|
|
@ -1,73 +0,0 @@
|
|||||||
package com.jessebrault.ssg.provider
|
|
||||||
|
|
||||||
import groovy.transform.EqualsAndHashCode
|
|
||||||
import groovy.transform.NullCheck
|
|
||||||
import groovy.transform.TupleConstructor
|
|
||||||
|
|
||||||
@TupleConstructor(defaults = false, includeFields = true)
|
|
||||||
@NullCheck(includeGenerated = true)
|
|
||||||
@EqualsAndHashCode(includeFields = true)
|
|
||||||
abstract class AbstractCollectionProvider<T> implements CollectionProvider<T> {
|
|
||||||
|
|
||||||
static <T> CollectionProvider<T> concat(
|
|
||||||
CollectionProvider<T> cp0,
|
|
||||||
CollectionProvider<T> cp1
|
|
||||||
) {
|
|
||||||
new SupplierBasedCollectionProvider<>([cp0, cp1], {
|
|
||||||
cp0.provide() + cp1.provide()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
private final Collection<CollectionProvider<T>> collectionProviderChildren
|
|
||||||
|
|
||||||
@Override
|
|
||||||
boolean contains(CollectionProvider<T> collectionProvider) {
|
|
||||||
collectionProvider in this
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
<C extends CollectionProvider<T>> boolean containsType(Class<C> childCollectionProviderClass) {
|
|
||||||
if (childCollectionProviderClass.isAssignableFrom(this.class)) {
|
|
||||||
true
|
|
||||||
} else {
|
|
||||||
this.collectionProviderChildren.inject(false) { acc, childProvider ->
|
|
||||||
acc || childCollectionProviderClass.isAssignableFrom(childProvider.class)
|
|
||||||
|| childProvider.containsType(childCollectionProviderClass)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
<C extends CollectionProvider<T>> Collection<C> getChildrenOfType(Class<C> childCollectionProviderClass) {
|
|
||||||
this.collectionProviderChildren.inject([] as Collection<C>) { acc, childProvider ->
|
|
||||||
if (childCollectionProviderClass.isAssignableFrom(childProvider.class)) {
|
|
||||||
acc + childProvider.getChildrenOfType(childCollectionProviderClass) + (childProvider as C)
|
|
||||||
} else {
|
|
||||||
acc + childProvider.getChildrenOfType(childCollectionProviderClass)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
Collection<DirectoryCollectionProvider<T>> getDirectoryCollectionProviderChildren() {
|
|
||||||
this.getChildrenOfType(DirectoryCollectionProvider)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
CollectionProvider<T> plus(CollectionProvider<T> other) {
|
|
||||||
concat(this, other)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
boolean isCase(CollectionProvider<T> collectionProvider) {
|
|
||||||
collectionProvider == this
|
|
||||||
|| collectionProvider in this.collectionProviderChildren
|
|
||||||
|| this.collectionProviderChildren.inject(
|
|
||||||
false,
|
|
||||||
{ acc, childCollectionProvider ->
|
|
||||||
acc || collectionProvider in childCollectionProvider
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,23 +0,0 @@
|
|||||||
package com.jessebrault.ssg.provider;
|
|
||||||
|
|
||||||
import org.jetbrains.annotations.ApiStatus;
|
|
||||||
|
|
||||||
import java.util.Collection;
|
|
||||||
|
|
||||||
public interface CollectionProvider<T> {
|
|
||||||
Collection<T> provide();
|
|
||||||
|
|
||||||
boolean contains(CollectionProvider<T> collectionProvider);
|
|
||||||
|
|
||||||
@ApiStatus.Experimental
|
|
||||||
<C extends CollectionProvider<T>> boolean containsType(Class<C> childCollectionProviderClass);
|
|
||||||
|
|
||||||
@ApiStatus.Experimental
|
|
||||||
<C extends CollectionProvider<T>> Collection<C> getChildrenOfType(Class<C> childCollectionProviderClass);
|
|
||||||
|
|
||||||
Collection<DirectoryCollectionProvider<T>> getDirectoryCollectionProviderChildren();
|
|
||||||
|
|
||||||
CollectionProvider<T> plus(CollectionProvider<T> other);
|
|
||||||
|
|
||||||
boolean isCase(CollectionProvider<T> collectionProvider);
|
|
||||||
}
|
|
@ -1,31 +0,0 @@
|
|||||||
package com.jessebrault.ssg.provider
|
|
||||||
|
|
||||||
import org.jetbrains.annotations.Nullable
|
|
||||||
|
|
||||||
import java.util.function.BiFunction
|
|
||||||
import java.util.function.Supplier
|
|
||||||
|
|
||||||
final class CollectionProviders {
|
|
||||||
|
|
||||||
static <T> CollectionProvider<T> getEmpty() {
|
|
||||||
new SimpleCollectionProvider<>([])
|
|
||||||
}
|
|
||||||
|
|
||||||
static <T> CollectionProvider<T> fromCollection(Collection<T> ts) {
|
|
||||||
new SimpleCollectionProvider<T>(ts)
|
|
||||||
}
|
|
||||||
|
|
||||||
static <T> CollectionProvider<T> fromSupplier(Supplier<Collection<T>> supplier) {
|
|
||||||
new SupplierBasedCollectionProvider<>(supplier)
|
|
||||||
}
|
|
||||||
|
|
||||||
static <T> DirectoryCollectionProvider<T> fromDirectory(
|
|
||||||
File baseDirectory,
|
|
||||||
BiFunction<File, String, @Nullable T> elementFunction
|
|
||||||
) {
|
|
||||||
new FileBasedCollectionProvider<>(baseDirectory, elementFunction)
|
|
||||||
}
|
|
||||||
|
|
||||||
private CollectionProviders() {}
|
|
||||||
|
|
||||||
}
|
|
@ -1,11 +0,0 @@
|
|||||||
package com.jessebrault.ssg.provider;
|
|
||||||
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.util.function.BiFunction;
|
|
||||||
|
|
||||||
public interface DirectoryCollectionProvider<T> extends CollectionProvider<T> {
|
|
||||||
File getBaseDirectory();
|
|
||||||
BiFunction<File, String, @Nullable T> getMapper();
|
|
||||||
}
|
|
@ -1,57 +0,0 @@
|
|||||||
package com.jessebrault.ssg.provider
|
|
||||||
|
|
||||||
import com.jessebrault.ssg.util.PathUtil
|
|
||||||
import groovy.io.FileType
|
|
||||||
import groovy.transform.EqualsAndHashCode
|
|
||||||
import groovy.transform.NullCheck
|
|
||||||
import groovy.transform.PackageScope
|
|
||||||
import org.jetbrains.annotations.Nullable
|
|
||||||
import org.slf4j.Logger
|
|
||||||
import org.slf4j.LoggerFactory
|
|
||||||
|
|
||||||
import java.util.function.BiFunction
|
|
||||||
|
|
||||||
@PackageScope
|
|
||||||
@NullCheck
|
|
||||||
@EqualsAndHashCode(includeFields = true, callSuper = true)
|
|
||||||
final class FileBasedCollectionProvider<T> extends AbstractCollectionProvider<T> implements DirectoryCollectionProvider<T> {
|
|
||||||
|
|
||||||
private static final Logger logger = LoggerFactory.getLogger(FileBasedCollectionProvider)
|
|
||||||
|
|
||||||
private final File baseDirectory
|
|
||||||
private final BiFunction<File, String, @Nullable T> elementFunction
|
|
||||||
|
|
||||||
FileBasedCollectionProvider(File baseDirectory, BiFunction<File, String, T> elementFunction) {
|
|
||||||
super([], [])
|
|
||||||
this.baseDirectory = baseDirectory
|
|
||||||
this.elementFunction = elementFunction
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
File getBaseDirectory() {
|
|
||||||
this.baseDirectory
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
BiFunction<File, String, T> getMapper() {
|
|
||||||
this.elementFunction
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
Collection<T> provide() {
|
|
||||||
if (!this.baseDirectory.isDirectory()) {
|
|
||||||
logger.warn('{} does not exist or is not a directory; returning empty collection', this.baseDirectory)
|
|
||||||
[]
|
|
||||||
} else {
|
|
||||||
final Collection<T> ts = []
|
|
||||||
this.baseDirectory.eachFileRecurse(FileType.FILES) {
|
|
||||||
def t = this.elementFunction.apply(it, PathUtil.relative(this.baseDirectory.path, it.path)) as T
|
|
||||||
if (t != null) {
|
|
||||||
ts << t
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ts
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,27 +0,0 @@
|
|||||||
package com.jessebrault.ssg.provider
|
|
||||||
|
|
||||||
import com.jessebrault.ssg.page.Page
|
|
||||||
import groovy.transform.TupleConstructor
|
|
||||||
import groowt.util.fp.provider.NamedProvider
|
|
||||||
import groowt.util.fp.provider.Provider
|
|
||||||
|
|
||||||
@TupleConstructor(includeFields = true, force = true, defaults = false)
|
|
||||||
class PageProvider implements NamedProvider<Page> {
|
|
||||||
|
|
||||||
final String name
|
|
||||||
final String path
|
|
||||||
|
|
||||||
private final Provider<Page> lazyPage
|
|
||||||
|
|
||||||
PageProvider(String name, String path, Page page) {
|
|
||||||
this.name = name
|
|
||||||
this.path = path
|
|
||||||
this.lazyPage = { page }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
Page get() {
|
|
||||||
this.lazyPage.get()
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,24 +0,0 @@
|
|||||||
package com.jessebrault.ssg.provider
|
|
||||||
|
|
||||||
import groovy.transform.EqualsAndHashCode
|
|
||||||
import groovy.transform.NullCheck
|
|
||||||
import groovy.transform.PackageScope
|
|
||||||
|
|
||||||
@PackageScope
|
|
||||||
@NullCheck
|
|
||||||
@EqualsAndHashCode(includeFields = true, callSuper = true)
|
|
||||||
final class SimpleCollectionProvider<T> extends AbstractCollectionProvider<T> {
|
|
||||||
|
|
||||||
private final Collection<T> ts
|
|
||||||
|
|
||||||
SimpleCollectionProvider(Collection<T> ts) {
|
|
||||||
super([], [])
|
|
||||||
this.ts = ts
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
Collection<T> provide() {
|
|
||||||
this.ts
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,33 +0,0 @@
|
|||||||
package com.jessebrault.ssg.provider
|
|
||||||
|
|
||||||
import groovy.transform.EqualsAndHashCode
|
|
||||||
import groovy.transform.NullCheck
|
|
||||||
import groovy.transform.PackageScope
|
|
||||||
|
|
||||||
import java.util.function.Supplier
|
|
||||||
|
|
||||||
@PackageScope
|
|
||||||
@NullCheck
|
|
||||||
@EqualsAndHashCode(includeFields = true, callSuper = true)
|
|
||||||
final class SupplierBasedCollectionProvider<T> extends AbstractCollectionProvider<T> {
|
|
||||||
|
|
||||||
private final Supplier<Collection<T>> supplier
|
|
||||||
|
|
||||||
SupplierBasedCollectionProvider(
|
|
||||||
Collection<CollectionProvider<T>> collectionProviderChildren,
|
|
||||||
Supplier<Collection<T>> supplier
|
|
||||||
) {
|
|
||||||
super(collectionProviderChildren)
|
|
||||||
this.supplier = supplier
|
|
||||||
}
|
|
||||||
|
|
||||||
SupplierBasedCollectionProvider(Supplier<Collection<T>> supplier) {
|
|
||||||
this([], supplier)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
Collection<T> provide() {
|
|
||||||
this.supplier.get()
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,39 +0,0 @@
|
|||||||
package com.jessebrault.ssg.render
|
|
||||||
|
|
||||||
import com.jessebrault.ssg.SiteSpec
|
|
||||||
import com.jessebrault.ssg.model.Model
|
|
||||||
import com.jessebrault.ssg.part.Part
|
|
||||||
import com.jessebrault.ssg.task.Task
|
|
||||||
import com.jessebrault.ssg.text.Text
|
|
||||||
import groovy.transform.EqualsAndHashCode
|
|
||||||
import groovy.transform.NullCheck
|
|
||||||
import groovy.transform.TupleConstructor
|
|
||||||
|
|
||||||
@TupleConstructor(defaults = false, force = true)
|
|
||||||
@NullCheck(includeGenerated = true)
|
|
||||||
@EqualsAndHashCode
|
|
||||||
final class RenderContext {
|
|
||||||
|
|
||||||
RenderContext(Map<String, Object> args = [:]) {
|
|
||||||
this(
|
|
||||||
args.sourcePath as String ?: '',
|
|
||||||
args.targetPath as String ?: '',
|
|
||||||
args.tasks as Collection<Task> ?: [],
|
|
||||||
args.texts as Collection<Text> ?: [],
|
|
||||||
args.models as Collection<Model<Object>> ?: [],
|
|
||||||
args.parts as Collection<Part> ?: [],
|
|
||||||
args.siteSpec as SiteSpec ?: SiteSpec.getBlank(),
|
|
||||||
args.globals as Map<String, Object> ?: [:]
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
final String sourcePath
|
|
||||||
final String targetPath
|
|
||||||
final Collection<Task> tasks
|
|
||||||
final Collection<Text> texts
|
|
||||||
final Collection<Model<Object>> models
|
|
||||||
final Collection<Part> parts
|
|
||||||
final SiteSpec siteSpec
|
|
||||||
final Map<String, Object> globals
|
|
||||||
|
|
||||||
}
|
|
@ -1,39 +0,0 @@
|
|||||||
package com.jessebrault.ssg.render
|
|
||||||
|
|
||||||
import com.jessebrault.gst.TemplateCreator
|
|
||||||
import com.jessebrault.gst.groovy.GroovyTemplateCreator
|
|
||||||
import com.jessebrault.gst.parser.ExtendedGstParser
|
|
||||||
import com.jessebrault.ssg.dsl.StandardDslMap
|
|
||||||
import com.jessebrault.ssg.util.Diagnostic
|
|
||||||
import com.jessebrault.ssg.util.Result
|
|
||||||
|
|
||||||
import java.util.function.Consumer
|
|
||||||
|
|
||||||
final class StandardGspRenderer {
|
|
||||||
|
|
||||||
private final TemplateCreator templateCreator
|
|
||||||
|
|
||||||
StandardGspRenderer(ClassLoader parentClassLoader) {
|
|
||||||
this.templateCreator = new GroovyTemplateCreator(ExtendedGstParser::new, parentClassLoader, true)
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<String> render(
|
|
||||||
String templateText,
|
|
||||||
RenderContext context,
|
|
||||||
Consumer<StandardDslMap.Builder> dslMapBuilderConsumer
|
|
||||||
) {
|
|
||||||
try {
|
|
||||||
def templateCreateResult = this.templateCreator.create(templateText)
|
|
||||||
if (templateCreateResult.hasDiagnostics()) {
|
|
||||||
Result.ofDiagnostics(templateCreateResult.diagnostics.collect {
|
|
||||||
new Diagnostic(it.message, it.exception)
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
Result.of(templateCreateResult.get().make(StandardDslMap.get(context, dslMapBuilderConsumer)))
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
Result.ofDiagnostics([new Diagnostic(e.message, e)])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,23 +0,0 @@
|
|||||||
package com.jessebrault.ssg.task
|
|
||||||
|
|
||||||
import com.jessebrault.ssg.model.Model
|
|
||||||
import com.jessebrault.ssg.part.Part
|
|
||||||
import com.jessebrault.ssg.provider.CollectionProvider
|
|
||||||
import com.jessebrault.ssg.provider.CollectionProviders
|
|
||||||
import com.jessebrault.ssg.text.Text
|
|
||||||
|
|
||||||
import static java.util.Objects.requireNonNull
|
|
||||||
|
|
||||||
abstract class AbstractRenderTaskFactory implements TaskFactory {
|
|
||||||
|
|
||||||
CollectionProvider<Text> allTextsProvider = CollectionProviders.getEmpty()
|
|
||||||
CollectionProvider<Model<Object>> allModelsProvider = CollectionProviders.getEmpty()
|
|
||||||
CollectionProvider<Part> allPartsProvider = CollectionProviders.getEmpty()
|
|
||||||
|
|
||||||
protected final void checkProviders() {
|
|
||||||
requireNonNull(this.allTextsProvider)
|
|
||||||
requireNonNull(this.allModelsProvider)
|
|
||||||
requireNonNull(this.allPartsProvider)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,19 +0,0 @@
|
|||||||
package com.jessebrault.ssg.task
|
|
||||||
|
|
||||||
import groovy.transform.EqualsAndHashCode
|
|
||||||
import groovy.transform.NullCheck
|
|
||||||
import groovy.transform.TupleConstructor
|
|
||||||
|
|
||||||
@TupleConstructor(defaults = false)
|
|
||||||
@NullCheck(includeGenerated = true)
|
|
||||||
@EqualsAndHashCode
|
|
||||||
abstract class AbstractTask implements Task {
|
|
||||||
|
|
||||||
final String name
|
|
||||||
|
|
||||||
@Override
|
|
||||||
String toString() {
|
|
||||||
"AbstractTask(name: ${ this.name })"
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,29 +0,0 @@
|
|||||||
package com.jessebrault.ssg.task
|
|
||||||
|
|
||||||
import com.jessebrault.ssg.util.Result
|
|
||||||
import groovy.transform.EqualsAndHashCode
|
|
||||||
import groovy.transform.NullCheck
|
|
||||||
import groovy.transform.PackageScope
|
|
||||||
import groovy.transform.stc.ClosureParams
|
|
||||||
import groovy.transform.stc.SimpleType
|
|
||||||
|
|
||||||
@PackageScope
|
|
||||||
@NullCheck
|
|
||||||
@EqualsAndHashCode(includeFields = true)
|
|
||||||
final class ClosureBasedTaskFactory implements TaskFactory {
|
|
||||||
|
|
||||||
private final Closure<Collection<Task>> closure
|
|
||||||
|
|
||||||
ClosureBasedTaskFactory(
|
|
||||||
@ClosureParams(value = SimpleType, options = 'com.jessebrault.ssg.task.TaskSpec')
|
|
||||||
Closure<Collection<Task>> closure
|
|
||||||
) {
|
|
||||||
this.closure = closure
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
Result<Collection<Task>> getTasks(TaskSpec taskSpec) {
|
|
||||||
this.closure(taskSpec)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,5 +0,0 @@
|
|||||||
package com.jessebrault.ssg.task
|
|
||||||
|
|
||||||
interface FileOutput extends TaskOutput {
|
|
||||||
File getFile()
|
|
||||||
}
|
|
@ -1,8 +0,0 @@
|
|||||||
package com.jessebrault.ssg.task
|
|
||||||
|
|
||||||
import com.jessebrault.ssg.util.Diagnostic
|
|
||||||
|
|
||||||
interface Task {
|
|
||||||
String getName()
|
|
||||||
Collection<Diagnostic> execute(Collection<Task> allTasks)
|
|
||||||
}
|
|
@ -1,17 +0,0 @@
|
|||||||
package com.jessebrault.ssg.task
|
|
||||||
|
|
||||||
import groovy.transform.stc.ClosureParams
|
|
||||||
import groovy.transform.stc.SimpleType
|
|
||||||
|
|
||||||
final class TaskFactories {
|
|
||||||
|
|
||||||
static TaskFactory of(
|
|
||||||
@ClosureParams(value = SimpleType, options = 'com.jessebrault.ssg.task.TaskSpec')
|
|
||||||
Closure<Collection<Void>> closure
|
|
||||||
) {
|
|
||||||
new ClosureBasedTaskFactory(closure)
|
|
||||||
}
|
|
||||||
|
|
||||||
private TaskFactories() {}
|
|
||||||
|
|
||||||
}
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user