Major work for v0.2.0; moved from lib to api; tests all working.
This commit is contained in:
parent
0ef4a3aafd
commit
db5d00bdca
5
TODO.md
5
TODO.md
@ -15,5 +15,10 @@ Here will be kept all of the various todos for this project, organized by releas
|
||||
// as well as some other information, perhaps such as the Type, extension, *etc.*
|
||||
```
|
||||
- [ ] Add `extensionUtil` object to dsl.
|
||||
- [ ] Investigate imports, including static, in scripts
|
||||
- [ ] Get rid of `taskTypes` DSL, replace with static import of task types to scripts
|
||||
- [ ] Plan out plugin system such that we can create custom providers of texts, data, etc.
|
||||
- [ ] Plan out `data` models DSL
|
||||
- [ ] Provide a way to override `ssgBuilds` variables from the cli.
|
||||
|
||||
### Fix
|
||||
|
22
api/build.gradle
Normal file
22
api/build.gradle
Normal file
@ -0,0 +1,22 @@
|
||||
plugins {
|
||||
id 'ssg.common'
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
// https://mvnrepository.com/artifact/org.apache.groovy/groovy-templates
|
||||
implementation 'org.apache.groovy:groovy-templates:4.0.9'
|
||||
|
||||
// https://mvnrepository.com/artifact/org.commonmark/commonmark
|
||||
implementation 'org.commonmark:commonmark:0.21.0'
|
||||
|
||||
// https://mvnrepository.com/artifact/org.commonmark/commonmark-ext-yaml-front-matter
|
||||
implementation 'org.commonmark:commonmark-ext-yaml-front-matter:0.21.0'
|
||||
}
|
||||
|
||||
jar {
|
||||
archivesBaseName = 'ssg-api'
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
package com.jessebrault.ssg
|
||||
|
||||
import com.jessebrault.ssg.buildscript.Build
|
||||
import com.jessebrault.ssg.task.Task
|
||||
import com.jessebrault.ssg.util.Result
|
||||
|
||||
interface BuildTasksConverter {
|
||||
Result<Collection<Task>> convert(Build buildScriptResult)
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
package com.jessebrault.ssg
|
||||
|
||||
import com.jessebrault.ssg.buildscript.Build
|
||||
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(Build buildScriptResult) {
|
||||
def taskSpec = new TaskSpec(
|
||||
buildScriptResult.name,
|
||||
buildScriptResult.outputDirFunction.apply(buildScriptResult).file,
|
||||
buildScriptResult.siteSpec,
|
||||
buildScriptResult.globals
|
||||
)
|
||||
Collection<Task> tasks = []
|
||||
Collection<Diagnostic> diagnostics = []
|
||||
|
||||
buildScriptResult.taskFactorySpecs.each {
|
||||
def factory = it.supplier.get()
|
||||
it.configureClosures.each { it(factory) }
|
||||
def result = factory.getTasks(taskSpec)
|
||||
diagnostics.addAll(result.diagnostics)
|
||||
tasks.addAll(result.get())
|
||||
}
|
||||
|
||||
Result.of(diagnostics, tasks)
|
||||
}
|
||||
|
||||
}
|
35
api/src/main/groovy/com/jessebrault/ssg/SiteSpec.groovy
Normal file
35
api/src/main/groovy/com/jessebrault/ssg/SiteSpec.groovy
Normal file
@ -0,0 +1,35 @@
|
||||
package com.jessebrault.ssg
|
||||
|
||||
import groovy.transform.EqualsAndHashCode
|
||||
import groovy.transform.NullCheck
|
||||
import groovy.transform.TupleConstructor
|
||||
|
||||
@TupleConstructor(defaults = false)
|
||||
@NullCheck(includeGenerated = true)
|
||||
@EqualsAndHashCode
|
||||
final class SiteSpec {
|
||||
|
||||
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
|
||||
|
||||
SiteSpec plus(SiteSpec other) {
|
||||
concat(this, other)
|
||||
}
|
||||
|
||||
@Override
|
||||
String toString() {
|
||||
"SiteSpec(${ this.name }, ${ this.baseUrl })"
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,93 @@
|
||||
package com.jessebrault.ssg.buildscript
|
||||
|
||||
import com.jessebrault.ssg.SiteSpec
|
||||
import com.jessebrault.ssg.task.TaskFactorySpec
|
||||
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 Build {
|
||||
|
||||
@TupleConstructor(defaults = false)
|
||||
@NullCheck(includeGenerated = true)
|
||||
@EqualsAndHashCode
|
||||
static final class AllBuilds {
|
||||
|
||||
static AllBuilds concat(AllBuilds ab0, AllBuilds ab1) {
|
||||
new AllBuilds(
|
||||
ab0.siteSpec + ab1.siteSpec,
|
||||
ab0.globals + ab1.globals,
|
||||
ab0.taskFactorySpecs + ab1.taskFactorySpecs
|
||||
)
|
||||
}
|
||||
|
||||
static AllBuilds getEmpty() {
|
||||
new AllBuilds(SiteSpec.getBlank(), [:], [])
|
||||
}
|
||||
|
||||
final SiteSpec siteSpec
|
||||
final Map<String, Object> globals
|
||||
final Collection<TaskFactorySpec> taskFactorySpecs
|
||||
|
||||
AllBuilds plus(AllBuilds other) {
|
||||
concat(this, other)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static Build getEmpty() {
|
||||
new Build(
|
||||
'',
|
||||
OutputDirFunctions.DEFAULT,
|
||||
SiteSpec.getBlank(),
|
||||
[:],
|
||||
[]
|
||||
)
|
||||
}
|
||||
|
||||
static Build get(Map<String, Object> args) {
|
||||
new Build(
|
||||
args?.name as String ?: '',
|
||||
args?.outputDirFunction as Function<Build, OutputDir> ?: OutputDirFunctions.DEFAULT,
|
||||
args?.siteSpec as SiteSpec ?: SiteSpec.getBlank(),
|
||||
args?.globals as Map<String, Object> ?: [:],
|
||||
args?.taskFactorySpecs as Collection<TaskFactorySpec> ?: []
|
||||
)
|
||||
}
|
||||
|
||||
static Build concat(Build b0, Build b1) {
|
||||
new Build(
|
||||
b1.name.blank ? b0.name : b1.name,
|
||||
OutputDirFunctions.concat(b0.outputDirFunction, b1.outputDirFunction),
|
||||
SiteSpec.concat(b0.siteSpec, b1.siteSpec),
|
||||
b0.globals + b1.globals,
|
||||
b0.taskFactorySpecs + b1.taskFactorySpecs
|
||||
)
|
||||
}
|
||||
|
||||
static Build from(AllBuilds allBuilds) {
|
||||
new Build(
|
||||
'',
|
||||
OutputDirFunctions.DEFAULT,
|
||||
allBuilds.siteSpec,
|
||||
allBuilds.globals,
|
||||
allBuilds.taskFactorySpecs
|
||||
)
|
||||
}
|
||||
|
||||
final String name
|
||||
final Function<Build, OutputDir> outputDirFunction
|
||||
final SiteSpec siteSpec
|
||||
final Map<String, Object> globals
|
||||
final Collection<TaskFactorySpec> taskFactorySpecs
|
||||
|
||||
Build plus(Build other) {
|
||||
concat(this, other)
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,62 @@
|
||||
package com.jessebrault.ssg.buildscript
|
||||
|
||||
|
||||
import com.jessebrault.ssg.buildscript.Build.AllBuilds
|
||||
import com.jessebrault.ssg.buildscript.dsl.AllBuildsDelegate
|
||||
import com.jessebrault.ssg.buildscript.dsl.BuildDelegate
|
||||
|
||||
abstract class BuildScriptBase extends Script {
|
||||
|
||||
private final Collection<AllBuildsDelegate> allBuildsDelegates = []
|
||||
private final Collection<BuildDelegate> buildDelegates = []
|
||||
|
||||
private int currentBuildNumber = 0
|
||||
|
||||
final AllBuilds defaultAllBuilds = AllBuilds.getEmpty()
|
||||
|
||||
void build(
|
||||
@DelegatesTo(value = BuildDelegate, strategy = Closure.DELEGATE_FIRST)
|
||||
Closure<Void> buildClosure
|
||||
) {
|
||||
this.build('build' + this.currentBuildNumber, buildClosure)
|
||||
}
|
||||
|
||||
void build(
|
||||
String name,
|
||||
@DelegatesTo(value = BuildDelegate, strategy = Closure.DELEGATE_FIRST)
|
||||
Closure<Void> buildClosure
|
||||
) {
|
||||
def d = new BuildDelegate().tap {
|
||||
it.name = name
|
||||
}
|
||||
buildClosure.setDelegate(d)
|
||||
buildClosure.setResolveStrategy(Closure.DELEGATE_FIRST)
|
||||
buildClosure()
|
||||
this.buildDelegates << d
|
||||
this.currentBuildNumber++
|
||||
}
|
||||
|
||||
void allBuilds(
|
||||
@DelegatesTo(value = AllBuildsDelegate, strategy = Closure.DELEGATE_FIRST)
|
||||
Closure<Void> allBuildsClosure
|
||||
) {
|
||||
def d = new AllBuildsDelegate()
|
||||
allBuildsClosure.setDelegate(d)
|
||||
allBuildsClosure.setResolveStrategy(Closure.DELEGATE_FIRST)
|
||||
allBuildsClosure()
|
||||
this.allBuildsDelegates << d
|
||||
}
|
||||
|
||||
Collection<Build> getBuilds() {
|
||||
def allBuilds = this.defaultAllBuilds
|
||||
this.allBuildsDelegates.each {
|
||||
allBuilds += it.getResult()
|
||||
}
|
||||
|
||||
def baseBuild = Build.from(allBuilds)
|
||||
this.buildDelegates.collect {
|
||||
baseBuild + it.getResult()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
package com.jessebrault.ssg.buildscript
|
||||
|
||||
import java.util.function.Consumer
|
||||
|
||||
interface BuildScriptConfiguratorFactory {
|
||||
Consumer<BuildScriptBase> get()
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
package com.jessebrault.ssg.buildscript
|
||||
|
||||
import groovy.transform.NullCheck
|
||||
import org.codehaus.groovy.control.CompilerConfiguration
|
||||
import org.codehaus.groovy.control.customizers.ImportCustomizer
|
||||
|
||||
import java.util.function.Consumer
|
||||
|
||||
@NullCheck
|
||||
final class BuildScriptUtil {
|
||||
|
||||
// TODO: check exactly what we are importing to the script automatically
|
||||
// TODO: check the roots arg, do we include 'ssgBuilds'/'buildSrc' dir eventually?
|
||||
static Collection<Build> runBuildScript(String relativePath, Consumer<BuildScriptBase> configureBuildScript) {
|
||||
def engine = new GroovyScriptEngine([new File('.').toURI().toURL()] as URL[])
|
||||
engine.config = new CompilerConfiguration().tap {
|
||||
addCompilationCustomizers(new ImportCustomizer().tap {
|
||||
addStarImports(
|
||||
'com.jessebrault.ssg',
|
||||
'com.jessebrault.ssg.part',
|
||||
'com.jessebrault.ssg.page',
|
||||
'com.jessebrault.ssg.template',
|
||||
'com.jessebrault.ssg.text',
|
||||
'com.jessebrault.ssg.util'
|
||||
)
|
||||
})
|
||||
scriptBaseClass = 'com.jessebrault.ssg.buildscript.BuildScriptBase'
|
||||
}
|
||||
|
||||
def buildScript = engine.createScript(relativePath, new Binding())
|
||||
assert buildScript instanceof BuildScriptBase
|
||||
configureBuildScript.accept(buildScript)
|
||||
buildScript()
|
||||
buildScript.getBuilds()
|
||||
}
|
||||
|
||||
private BuildScriptUtil() {}
|
||||
|
||||
}
|
@ -0,0 +1,97 @@
|
||||
package com.jessebrault.ssg.buildscript
|
||||
|
||||
import com.jessebrault.ssg.html.PageToHtmlTaskFactory
|
||||
import com.jessebrault.ssg.html.TextToHtmlSpec
|
||||
import com.jessebrault.ssg.html.TextToHtmlTaskFactory
|
||||
import com.jessebrault.ssg.page.PageTypes
|
||||
import com.jessebrault.ssg.page.PagesProviders
|
||||
import com.jessebrault.ssg.part.Part
|
||||
import com.jessebrault.ssg.part.PartTypes
|
||||
import com.jessebrault.ssg.provider.CollectionProviders
|
||||
import com.jessebrault.ssg.template.TemplatesProviders
|
||||
import com.jessebrault.ssg.text.TextsProviders
|
||||
import com.jessebrault.ssg.template.Template
|
||||
import com.jessebrault.ssg.template.TemplateTypes
|
||||
import com.jessebrault.ssg.text.TextTypes
|
||||
import com.jessebrault.ssg.util.ExtensionUtil
|
||||
import com.jessebrault.ssg.util.PathUtil
|
||||
import com.jessebrault.ssg.util.Result
|
||||
import org.slf4j.Logger
|
||||
import org.slf4j.LoggerFactory
|
||||
|
||||
import java.util.function.Consumer
|
||||
|
||||
final class DefaultBuildScriptConfiguratorFactory implements BuildScriptConfiguratorFactory {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(DefaultBuildScriptConfiguratorFactory)
|
||||
|
||||
@Override
|
||||
Consumer<BuildScriptBase> get() {
|
||||
return {
|
||||
it.allBuilds {
|
||||
types {
|
||||
textTypes << TextTypes.MARKDOWN
|
||||
pageTypes << PageTypes.GSP
|
||||
templateTypes << TemplateTypes.GSP
|
||||
partTypes << PartTypes.GSP
|
||||
|
||||
//noinspection GroovyUnnecessaryReturn
|
||||
return
|
||||
}
|
||||
|
||||
providers { types ->
|
||||
texts(TextsProviders.from(new File('texts'), types.textTypes))
|
||||
pages(PagesProviders.from(new File('pages'), types.pageTypes))
|
||||
templates(TemplatesProviders.from(new File('templates'), types.templateTypes))
|
||||
|
||||
parts(CollectionProviders.from(new File('parts')) { File file ->
|
||||
def extension = ExtensionUtil.getExtension(file.path)
|
||||
def partType = types.partTypes.find { it.ids.contains(extension) }
|
||||
if (!partType) {
|
||||
logger.warn('there is no PartType for file {}; skipping', file)
|
||||
null
|
||||
} else {
|
||||
new Part(PathUtil.relative('parts', file.path), partType, file.getText())
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
taskFactories { sp ->
|
||||
register('textToHtml', TextToHtmlTaskFactory::new) {
|
||||
it.specProvider += CollectionProviders.from {
|
||||
def templates = sp.templatesProvider.provide()
|
||||
sp.textsProvider.provide().collect {
|
||||
def frontMatterResult = it.type.frontMatterGetter.get(it)
|
||||
if (frontMatterResult.hasDiagnostics()) {
|
||||
return Result.ofDiagnostics(frontMatterResult.diagnostics)
|
||||
}
|
||||
def templateValue = frontMatterResult.get().get('template')
|
||||
if (templateValue) {
|
||||
def template = templates.find { it.path == templateValue }
|
||||
return Result.of(new TextToHtmlSpec(it, template, it.path))
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
}
|
||||
}
|
||||
it.allTextsProvider += sp.textsProvider
|
||||
it.allPartsProvider += sp.partsProvider
|
||||
|
||||
//noinspection GroovyUnnecessaryReturn
|
||||
return
|
||||
}
|
||||
|
||||
register('pageToHtml', PageToHtmlTaskFactory::new) {
|
||||
it.pagesProvider += sp.pagesProvider
|
||||
it.allTextsProvider += sp.textsProvider
|
||||
it.allPartsProvider += sp.partsProvider
|
||||
|
||||
//noinspection GroovyUnnecessaryReturn
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
package com.jessebrault.ssg.buildscript
|
||||
|
||||
import groovy.transform.EqualsAndHashCode
|
||||
import org.jetbrains.annotations.Nullable
|
||||
|
||||
@EqualsAndHashCode
|
||||
final class OutputDir {
|
||||
|
||||
static OutputDir concat(OutputDir od0, OutputDir od1) {
|
||||
new OutputDir(od1.path ? od1.path : od0.path)
|
||||
}
|
||||
|
||||
@Nullable
|
||||
final String path
|
||||
|
||||
OutputDir(@Nullable String path) {
|
||||
this.path = path
|
||||
}
|
||||
|
||||
OutputDir(File file) {
|
||||
this.path = file.path
|
||||
}
|
||||
|
||||
File getFile() {
|
||||
this.path ? new File(this.path) : new File('')
|
||||
}
|
||||
|
||||
OutputDir plus(OutputDir other) {
|
||||
concat(this, other)
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
package com.jessebrault.ssg.buildscript
|
||||
|
||||
import groovy.transform.stc.ClosureParams
|
||||
import groovy.transform.stc.SimpleType
|
||||
|
||||
import java.util.function.Function
|
||||
|
||||
final class OutputDirFunctions {
|
||||
|
||||
static final Function<Build, OutputDir> DEFAULT = of { new OutputDir(it.name) }
|
||||
|
||||
static Function<Build, OutputDir> concat(
|
||||
Function<Build, OutputDir> f0,
|
||||
Function<Build, OutputDir> f1
|
||||
) {
|
||||
f1 == OutputDirFunctions.DEFAULT ? f0 : f1
|
||||
}
|
||||
|
||||
static Function<Build, OutputDir> of(
|
||||
@ClosureParams(value = SimpleType, options = 'com.jessebrault.ssg.buildscript.Build')
|
||||
Closure<OutputDir> closure
|
||||
) {
|
||||
closure as Function<Build, OutputDir>
|
||||
}
|
||||
|
||||
static Function<Build, OutputDir> of(File dir) {
|
||||
of { new OutputDir(dir) }
|
||||
}
|
||||
|
||||
static Function<Build, OutputDir> of(String path) {
|
||||
of { new OutputDir(path) }
|
||||
}
|
||||
|
||||
private OutputDirFunctions() {}
|
||||
|
||||
}
|
@ -0,0 +1,64 @@
|
||||
package com.jessebrault.ssg.buildscript
|
||||
|
||||
import com.jessebrault.ssg.model.Model
|
||||
import com.jessebrault.ssg.page.Page
|
||||
import com.jessebrault.ssg.part.Part
|
||||
import com.jessebrault.ssg.provider.CollectionProvider
|
||||
import com.jessebrault.ssg.provider.CollectionProviders
|
||||
import com.jessebrault.ssg.template.Template
|
||||
import com.jessebrault.ssg.text.Text
|
||||
import groovy.transform.EqualsAndHashCode
|
||||
import groovy.transform.NullCheck
|
||||
import groovy.transform.TupleConstructor
|
||||
|
||||
@TupleConstructor(defaults = false)
|
||||
@NullCheck(includeGenerated = true)
|
||||
@EqualsAndHashCode
|
||||
final class SourceProviders {
|
||||
|
||||
static SourceProviders concat(SourceProviders sp0, SourceProviders sp1) {
|
||||
new SourceProviders(
|
||||
sp0.textsProvider + sp1.textsProvider,
|
||||
sp0.modelsProvider + sp1.modelsProvider,
|
||||
sp0.pagesProvider + sp1.pagesProvider,
|
||||
sp0.templatesProvider + sp1.templatesProvider,
|
||||
sp0.partsProvider + sp1.partsProvider
|
||||
)
|
||||
}
|
||||
|
||||
static SourceProviders get(Map<String, Object> args) {
|
||||
new SourceProviders(
|
||||
args?.textsProvider as CollectionProvider<Text>
|
||||
?: CollectionProviders.getEmpty() as CollectionProvider<Text>,
|
||||
args?.modelsProvider as CollectionProvider<Model<Object>>
|
||||
?: CollectionProviders.getEmpty() as CollectionProvider<Model<Object>>,
|
||||
args?.pagesProvider as CollectionProvider<Page>
|
||||
?: CollectionProviders.getEmpty() as CollectionProvider<Page>,
|
||||
args?.templatesProvider as CollectionProvider<Template>
|
||||
?: CollectionProviders.getEmpty() as CollectionProvider<Template>,
|
||||
args?.partsProvider as CollectionProvider<Part>
|
||||
?: CollectionProviders.getEmpty() as CollectionProvider<Part>
|
||||
)
|
||||
}
|
||||
|
||||
static SourceProviders getEmpty() {
|
||||
new SourceProviders(
|
||||
CollectionProviders.getEmpty(),
|
||||
CollectionProviders.getEmpty(),
|
||||
CollectionProviders.getEmpty(),
|
||||
CollectionProviders.getEmpty(),
|
||||
CollectionProviders.getEmpty()
|
||||
)
|
||||
}
|
||||
|
||||
final CollectionProvider<Text> textsProvider
|
||||
final CollectionProvider<Model<Object>> modelsProvider
|
||||
final CollectionProvider<Page> pagesProvider
|
||||
final CollectionProvider<Template> templatesProvider
|
||||
final CollectionProvider<Part> partsProvider
|
||||
|
||||
SourceProviders plus(SourceProviders other) {
|
||||
concat(this, other)
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
package com.jessebrault.ssg.buildscript
|
||||
|
||||
import com.jessebrault.ssg.page.PageType
|
||||
import com.jessebrault.ssg.part.PartType
|
||||
import com.jessebrault.ssg.template.TemplateType
|
||||
import com.jessebrault.ssg.text.TextType
|
||||
import groovy.transform.EqualsAndHashCode
|
||||
import groovy.transform.NullCheck
|
||||
import groovy.transform.TupleConstructor
|
||||
|
||||
@TupleConstructor(defaults = false)
|
||||
@NullCheck(includeGenerated = true)
|
||||
@EqualsAndHashCode
|
||||
final class TypesContainer {
|
||||
|
||||
static TypesContainer getEmpty() {
|
||||
new TypesContainer([], [], [], [])
|
||||
}
|
||||
|
||||
static TypesContainer concat(TypesContainer tc0, TypesContainer tc1) {
|
||||
new TypesContainer(
|
||||
tc0.textTypes + tc1.textTypes,
|
||||
tc0.pageTypes + tc1.pageTypes,
|
||||
tc0.templateTypes + tc1.templateTypes,
|
||||
tc0.partTypes + tc1.partTypes
|
||||
)
|
||||
}
|
||||
|
||||
Collection<TextType> textTypes
|
||||
Collection<PageType> pageTypes
|
||||
Collection<TemplateType> templateTypes
|
||||
Collection<PartType> partTypes
|
||||
|
||||
TypesContainer plus(TypesContainer other) {
|
||||
concat(this, other)
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,111 @@
|
||||
package com.jessebrault.ssg.buildscript.dsl
|
||||
|
||||
import com.jessebrault.ssg.SiteSpec
|
||||
import com.jessebrault.ssg.buildscript.SourceProviders
|
||||
import com.jessebrault.ssg.buildscript.TypesContainer
|
||||
import com.jessebrault.ssg.task.TaskFactorySpec
|
||||
import groovy.transform.stc.ClosureParams
|
||||
import groovy.transform.stc.SimpleType
|
||||
|
||||
abstract class AbstractBuildDelegate<T> {
|
||||
|
||||
private final Collection<Closure<Void>> siteSpecClosures = []
|
||||
private final Collection<Closure<Void>> globalsClosures = []
|
||||
private final Collection<Closure<Void>> typesClosures = []
|
||||
private final Collection<Closure<Void>> sourcesClosures = []
|
||||
private final Collection<Closure<Void>> taskFactoriesClosures = []
|
||||
|
||||
abstract T getResult()
|
||||
|
||||
protected final SiteSpec getSiteSpecResult() {
|
||||
this.siteSpecClosures.inject(SiteSpec.getBlank()) { acc, closure ->
|
||||
def d = new SiteSpecDelegate()
|
||||
closure.delegate = d
|
||||
closure.resolveStrategy = DELEGATE_FIRST
|
||||
closure()
|
||||
acc + d.getResult()
|
||||
}
|
||||
}
|
||||
|
||||
protected final Map<String, Object> getGlobalsResult() {
|
||||
this.globalsClosures.inject([:] as Map<String, Object>) { acc, closure ->
|
||||
def d = new GlobalsDelegate()
|
||||
closure.delegate = d
|
||||
closure.resolveStrategy = DELEGATE_FIRST
|
||||
closure()
|
||||
acc + d.getResult()
|
||||
}
|
||||
}
|
||||
|
||||
protected final TypesContainer getTypesResult() {
|
||||
this.typesClosures.inject(TypesContainer.getEmpty()) { acc, closure ->
|
||||
def d = new TypesDelegate()
|
||||
closure.delegate = d
|
||||
closure.resolveStrategy = DELEGATE_FIRST
|
||||
closure()
|
||||
acc + d.getResult()
|
||||
}
|
||||
}
|
||||
|
||||
protected final SourceProviders getSourcesResult(TypesContainer typesContainer) {
|
||||
this.sourcesClosures.inject(SourceProviders.getEmpty()) { acc, closure ->
|
||||
def d = new SourceProvidersDelegate()
|
||||
closure.delegate = d
|
||||
closure.resolveStrategy = DELEGATE_FIRST
|
||||
closure(typesContainer)
|
||||
acc + d.getResult()
|
||||
}
|
||||
}
|
||||
|
||||
protected final Collection<TaskFactorySpec> getTaskFactoriesResult(SourceProviders sourceProviders) {
|
||||
this.taskFactoriesClosures.inject([:] as Map<String, TaskFactorySpec>) { acc, closure ->
|
||||
def d = new TaskFactoriesDelegate()
|
||||
closure.delegate = d
|
||||
closure.resolveStrategy = DELEGATE_FIRST
|
||||
closure(sourceProviders)
|
||||
def specs = d.getResult()
|
||||
specs.forEach { name, spec ->
|
||||
acc.merge(name, spec) { spec0, spec1 -> spec0 + spec1 }
|
||||
}
|
||||
acc
|
||||
}.values()
|
||||
}
|
||||
|
||||
void siteSpec(
|
||||
@DelegatesTo(value = SiteSpecDelegate, strategy = Closure.DELEGATE_FIRST)
|
||||
Closure<Void> siteSpecClosure
|
||||
) {
|
||||
this.siteSpecClosures << siteSpecClosure
|
||||
}
|
||||
|
||||
void globals(
|
||||
@DelegatesTo(value = GlobalsDelegate, strategy = Closure.DELEGATE_FIRST)
|
||||
Closure<Void> globalsClosure
|
||||
) {
|
||||
this.globalsClosures << globalsClosure
|
||||
}
|
||||
|
||||
void types(
|
||||
@DelegatesTo(value = TypesDelegate, strategy = Closure.DELEGATE_FIRST)
|
||||
Closure<Void> typesClosure
|
||||
) {
|
||||
this.typesClosures << typesClosure
|
||||
}
|
||||
|
||||
void providers(
|
||||
@DelegatesTo(value = SourceProvidersDelegate, strategy = Closure.DELEGATE_FIRST)
|
||||
@ClosureParams(value = SimpleType, options = 'com.jessebrault.ssg.buildscript.TypesContainer')
|
||||
Closure<Void> providersClosure
|
||||
) {
|
||||
this.sourcesClosures << providersClosure
|
||||
}
|
||||
|
||||
void taskFactories(
|
||||
@DelegatesTo(value = TaskFactoriesDelegate, strategy = Closure.DELEGATE_FIRST)
|
||||
@ClosureParams(value = SimpleType, options = 'com.jessebrault.ssg.buildscript.SourceProviders')
|
||||
Closure<Void> taskFactoriesClosure
|
||||
) {
|
||||
this.taskFactoriesClosures << taskFactoriesClosure
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
package com.jessebrault.ssg.buildscript.dsl
|
||||
|
||||
import com.jessebrault.ssg.buildscript.Build
|
||||
|
||||
final class AllBuildsDelegate extends AbstractBuildDelegate<Build.AllBuilds> {
|
||||
|
||||
@Override
|
||||
Build.AllBuilds getResult() {
|
||||
new Build.AllBuilds(
|
||||
this.getSiteSpecResult(),
|
||||
this.getGlobalsResult(),
|
||||
this.getTaskFactoriesResult(this.getSourcesResult(this.getTypesResult()))
|
||||
)
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
package com.jessebrault.ssg.buildscript.dsl
|
||||
|
||||
import com.jessebrault.ssg.buildscript.Build
|
||||
import com.jessebrault.ssg.buildscript.OutputDir
|
||||
import com.jessebrault.ssg.buildscript.OutputDirFunctions
|
||||
import org.jetbrains.annotations.Nullable
|
||||
|
||||
import java.util.function.Function
|
||||
|
||||
final class BuildDelegate extends AbstractBuildDelegate<Build> {
|
||||
|
||||
String name = ''
|
||||
private Function<Build, OutputDir> outputDirFunction = OutputDirFunctions.DEFAULT
|
||||
|
||||
@Override
|
||||
Build getResult() {
|
||||
new Build(
|
||||
this.name,
|
||||
this.outputDirFunction,
|
||||
this.getSiteSpecResult(),
|
||||
this.getGlobalsResult(),
|
||||
this.getTaskFactoriesResult(this.getSourcesResult(this.getTypesResult()))
|
||||
)
|
||||
}
|
||||
|
||||
void setOutputDirFunction(Function<Build, OutputDir> outputDirFunction) {
|
||||
this.outputDirFunction = outputDirFunction
|
||||
}
|
||||
|
||||
void setOutputDir(File file) {
|
||||
this.outputDirFunction = { new OutputDir(file) }
|
||||
}
|
||||
|
||||
void setOutputDir(@Nullable String path) {
|
||||
this.outputDirFunction = { new OutputDir(path) }
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
package com.jessebrault.ssg.buildscript.dsl
|
||||
|
||||
final class GlobalsDelegate {
|
||||
|
||||
@Delegate
|
||||
final Map<String, Object> globals = [:]
|
||||
|
||||
@Override
|
||||
Object getProperty(String propertyName) {
|
||||
this.globals[propertyName]
|
||||
}
|
||||
|
||||
@Override
|
||||
void setProperty(String propertyName, Object newValue) {
|
||||
this.globals[propertyName] = newValue
|
||||
}
|
||||
|
||||
Map<String, Object> getResult() {
|
||||
this.globals
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
package com.jessebrault.ssg.buildscript.dsl
|
||||
|
||||
import com.jessebrault.ssg.SiteSpec
|
||||
|
||||
final class SiteSpecDelegate {
|
||||
|
||||
String name
|
||||
String baseUrl
|
||||
|
||||
SiteSpecDelegate() {
|
||||
def blank = SiteSpec.getBlank()
|
||||
this.name = blank.name
|
||||
this.baseUrl = blank.baseUrl
|
||||
}
|
||||
|
||||
SiteSpec getResult() {
|
||||
new SiteSpec(this.name, this.baseUrl)
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
package com.jessebrault.ssg.buildscript.dsl
|
||||
|
||||
import com.jessebrault.ssg.buildscript.SourceProviders
|
||||
import com.jessebrault.ssg.model.Model
|
||||
import com.jessebrault.ssg.page.Page
|
||||
import com.jessebrault.ssg.part.Part
|
||||
import com.jessebrault.ssg.provider.CollectionProvider
|
||||
import com.jessebrault.ssg.provider.CollectionProviders
|
||||
import com.jessebrault.ssg.template.Template
|
||||
import com.jessebrault.ssg.text.Text
|
||||
|
||||
final class SourceProvidersDelegate {
|
||||
|
||||
private CollectionProvider<Text> textsProvider = CollectionProviders.getEmpty()
|
||||
private CollectionProvider<Model<Object>> modelsProvider = CollectionProviders.getEmpty()
|
||||
private CollectionProvider<Page> pagesProvider = CollectionProviders.getEmpty()
|
||||
private CollectionProvider<Template> templatesProvider = CollectionProviders.getEmpty()
|
||||
private CollectionProvider<Part> partsProvider = CollectionProviders.getEmpty()
|
||||
|
||||
void texts(CollectionProvider<Text> textsProvider) {
|
||||
this.textsProvider += textsProvider
|
||||
}
|
||||
|
||||
void models(CollectionProvider<Model<?>> modelsProvider) {
|
||||
this.modelsProvider += modelsProvider
|
||||
}
|
||||
|
||||
void pages(CollectionProvider<Page> pagesProvider) {
|
||||
this.pagesProvider += pagesProvider
|
||||
}
|
||||
|
||||
void templates(CollectionProvider<Template> templatesProvider) {
|
||||
this.templatesProvider += templatesProvider
|
||||
}
|
||||
|
||||
void parts(CollectionProvider<Part> partsProvider) {
|
||||
this.partsProvider += partsProvider
|
||||
}
|
||||
|
||||
SourceProviders getResult() {
|
||||
new SourceProviders(
|
||||
this.textsProvider,
|
||||
this.modelsProvider,
|
||||
this.pagesProvider,
|
||||
this.templatesProvider,
|
||||
this.partsProvider
|
||||
)
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
package com.jessebrault.ssg.buildscript.dsl
|
||||
|
||||
import com.jessebrault.ssg.task.TaskFactory
|
||||
import com.jessebrault.ssg.task.TaskFactorySpec
|
||||
import groovy.transform.stc.ClosureParams
|
||||
import groovy.transform.stc.SecondParam
|
||||
|
||||
import java.util.function.Supplier
|
||||
|
||||
final class TaskFactoriesDelegate {
|
||||
|
||||
private final Map<String, TaskFactorySpec> specs = [:]
|
||||
|
||||
private void checkNotRegistered(String name) {
|
||||
if (this.specs.containsKey(name)) {
|
||||
throw new IllegalArgumentException("a TaskFactory is already registered by the name ${ name }")
|
||||
}
|
||||
}
|
||||
|
||||
void register(String name, Supplier<? extends TaskFactory> factorySupplier) {
|
||||
this.checkNotRegistered(name)
|
||||
this.specs[name] = new TaskFactorySpec(factorySupplier, [])
|
||||
}
|
||||
|
||||
def <T extends TaskFactory> void register(
|
||||
String name,
|
||||
Supplier<T> factorySupplier,
|
||||
@ClosureParams(value = SecondParam.FirstGenericType)
|
||||
Closure<Void> factoryConfigureClosure
|
||||
) {
|
||||
this.checkNotRegistered(name)
|
||||
this.specs[name] = new TaskFactorySpec(factorySupplier, [factoryConfigureClosure])
|
||||
}
|
||||
|
||||
void configure(String name, Closure<Void> factoryConfigureClosure) {
|
||||
if (!this.specs.containsKey(name)) {
|
||||
throw new IllegalArgumentException("there is no TaskFactory registered by name ${ name }")
|
||||
}
|
||||
this.specs[name].configureClosures << factoryConfigureClosure
|
||||
}
|
||||
|
||||
Map<String, TaskFactorySpec> getResult() {
|
||||
this.specs
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
package com.jessebrault.ssg.buildscript.dsl
|
||||
|
||||
import com.jessebrault.ssg.buildscript.TypesContainer
|
||||
import com.jessebrault.ssg.page.PageType
|
||||
import com.jessebrault.ssg.part.PartType
|
||||
import com.jessebrault.ssg.template.TemplateType
|
||||
import com.jessebrault.ssg.text.TextType
|
||||
import groovy.transform.EqualsAndHashCode
|
||||
import groovy.transform.NullCheck
|
||||
|
||||
@NullCheck
|
||||
@EqualsAndHashCode
|
||||
final class TypesDelegate {
|
||||
|
||||
final Collection<TextType> textTypes = []
|
||||
final Collection<PageType> pageTypes = []
|
||||
final Collection<TemplateType> templateTypes = []
|
||||
final Collection<PartType> partTypes = []
|
||||
|
||||
TypesContainer getResult() {
|
||||
new TypesContainer(this.textTypes, this.pageTypes, this.templateTypes, this.partTypes)
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
package com.jessebrault.ssg.dsl
|
||||
|
||||
import com.jessebrault.ssg.part.Part
|
||||
import com.jessebrault.ssg.render.RenderContext
|
||||
import com.jessebrault.ssg.text.Text
|
||||
import groovy.transform.EqualsAndHashCode
|
||||
import org.jetbrains.annotations.Nullable
|
||||
|
||||
import static java.util.Objects.requireNonNull
|
||||
|
||||
@EqualsAndHashCode(includeFields = true)
|
||||
final class EmbeddablePart {
|
||||
|
||||
private final Part part
|
||||
private final RenderContext context
|
||||
private final Closure<Void> onDiagnostics
|
||||
|
||||
@Nullable
|
||||
private final Text text
|
||||
|
||||
EmbeddablePart(
|
||||
Part part,
|
||||
RenderContext context,
|
||||
Closure<Void> onDiagnostics,
|
||||
@Nullable Text text
|
||||
) {
|
||||
this.part = requireNonNull(part)
|
||||
this.context = requireNonNull(context)
|
||||
this.onDiagnostics = requireNonNull(onDiagnostics)
|
||||
this.text = text
|
||||
}
|
||||
|
||||
String render(Map binding = [:]) {
|
||||
def result = part.type.renderer.render(
|
||||
this.part,
|
||||
binding,
|
||||
this.context,
|
||||
this.text
|
||||
)
|
||||
if (result.hasDiagnostics()) {
|
||||
this.onDiagnostics.call(result.diagnostics)
|
||||
''
|
||||
} else {
|
||||
result.get()
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
String toString() {
|
||||
"EmbeddablePart(part: ${ this.part })"
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
package com.jessebrault.ssg.dsl
|
||||
|
||||
import com.jessebrault.ssg.render.RenderContext
|
||||
import com.jessebrault.ssg.text.Text
|
||||
import groovy.transform.EqualsAndHashCode
|
||||
import org.jetbrains.annotations.Nullable
|
||||
|
||||
import static java.util.Objects.requireNonNull
|
||||
|
||||
@EqualsAndHashCode(includeFields = true)
|
||||
final class EmbeddablePartsMap {
|
||||
|
||||
@Delegate
|
||||
private final Map<String, EmbeddablePart> partsMap = [:]
|
||||
|
||||
EmbeddablePartsMap(
|
||||
RenderContext context,
|
||||
Closure<Void> onDiagnostics,
|
||||
@Nullable Text text = null
|
||||
) {
|
||||
requireNonNull(context)
|
||||
requireNonNull(onDiagnostics)
|
||||
context.parts.each {
|
||||
this.put(it.path, new EmbeddablePart(it, context, onDiagnostics, text))
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
String toString() {
|
||||
"EmbeddablePartsMap(partsMap: ${ this.partsMap })"
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,60 @@
|
||||
package com.jessebrault.ssg.dsl
|
||||
|
||||
import com.jessebrault.ssg.text.FrontMatter
|
||||
import com.jessebrault.ssg.text.Text
|
||||
import groovy.transform.EqualsAndHashCode
|
||||
import groovy.transform.Memoized
|
||||
import groovy.transform.NullCheck
|
||||
import groovy.transform.TupleConstructor
|
||||
|
||||
@TupleConstructor(includeFields = true, defaults = false)
|
||||
@NullCheck(includeGenerated = true)
|
||||
@EqualsAndHashCode(includeFields = true)
|
||||
final class EmbeddableText {
|
||||
|
||||
private final Text text
|
||||
private final Closure<Void> onDiagnostics
|
||||
|
||||
@Memoized
|
||||
String render() {
|
||||
def result = this.text.type.renderer.render(this.text)
|
||||
if (result.diagnostics.size() > 0) {
|
||||
this.onDiagnostics.call(result.diagnostics)
|
||||
''
|
||||
} else {
|
||||
result.get()
|
||||
}
|
||||
}
|
||||
|
||||
@Memoized
|
||||
FrontMatter getFrontMatter() {
|
||||
def result = this.text.type.frontMatterGetter.get(this.text)
|
||||
if (result.hasDiagnostics()) {
|
||||
this.onDiagnostics.call(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.onDiagnostics.call(result.diagnostics)
|
||||
''
|
||||
} else {
|
||||
result.get()
|
||||
}
|
||||
}
|
||||
|
||||
String getPath() {
|
||||
this.text.path
|
||||
}
|
||||
|
||||
@Override
|
||||
String toString() {
|
||||
"EmbeddableText(text: ${ this.text })"
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
package com.jessebrault.ssg.dsl
|
||||
|
||||
import com.jessebrault.ssg.text.Text
|
||||
import groovy.transform.EqualsAndHashCode
|
||||
import groovy.transform.NullCheck
|
||||
|
||||
@NullCheck
|
||||
@EqualsAndHashCode(includeFields = true)
|
||||
final class EmbeddableTextsCollection {
|
||||
|
||||
@Delegate
|
||||
private final Collection<EmbeddableText> embeddableTexts = []
|
||||
|
||||
EmbeddableTextsCollection(Collection<Text> texts, Closure<Void> onDiagnostics) {
|
||||
texts.each {
|
||||
this << new EmbeddableText(it, onDiagnostics)
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
String toString() {
|
||||
"EmbeddableTextsCollection(embeddableTexts: ${ this.embeddableTexts })"
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
package com.jessebrault.ssg.dsl
|
||||
|
||||
import com.jessebrault.ssg.model.Model
|
||||
import groovy.transform.EqualsAndHashCode
|
||||
import groovy.transform.NullCheck
|
||||
import org.jetbrains.annotations.Nullable
|
||||
|
||||
@NullCheck
|
||||
@EqualsAndHashCode(includeFields = true)
|
||||
final class ModelCollection<T> {
|
||||
|
||||
@Delegate
|
||||
private final Collection<Model<T>> ts = []
|
||||
|
||||
ModelCollection(Collection<Model<T>> ts) {
|
||||
this.ts.addAll(ts)
|
||||
}
|
||||
|
||||
@Nullable
|
||||
Model<T> getByName(String name) {
|
||||
this.ts.find { it.name == name }
|
||||
}
|
||||
|
||||
@Nullable
|
||||
<E extends T> Model<E> getByNameAndType(String name, Class<E> type) {
|
||||
this.ts.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 extends T> ModelCollection<E> findAllByType(Class<E> type) {
|
||||
def es = this.ts.findResults {
|
||||
type.isAssignableFrom(it.get().class) ? it as Model<E> : null
|
||||
}
|
||||
new ModelCollection<>(es)
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,87 @@
|
||||
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 groovy.transform.NullCheck
|
||||
import groovy.transform.stc.ClosureParams
|
||||
import groovy.transform.stc.SimpleType
|
||||
import org.slf4j.LoggerFactory
|
||||
|
||||
final class StandardDslMap {
|
||||
|
||||
@NullCheck(includeGenerated = true)
|
||||
static final class Builder {
|
||||
|
||||
private final Map<String, Object> custom = [:]
|
||||
|
||||
String loggerName = ''
|
||||
Closure<Void> onDiagnostics = { }
|
||||
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,
|
||||
@DelegatesTo(value = Builder, strategy = Closure.DELEGATE_FIRST)
|
||||
@ClosureParams(
|
||||
value = SimpleType,
|
||||
options = ['com.jessebrault.ssg.dsl.StandardDslMap.Builder']
|
||||
)
|
||||
Closure<Void> builderClosure
|
||||
) {
|
||||
def b = new Builder()
|
||||
builderClosure.resolveStrategy = Closure.DELEGATE_FIRST
|
||||
builderClosure.delegate = b
|
||||
builderClosure(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.onDiagnostics,
|
||||
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.onDiagnostics
|
||||
) : null
|
||||
it.texts = new EmbeddableTextsCollection(
|
||||
context.texts,
|
||||
b.onDiagnostics
|
||||
)
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
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>
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,77 @@
|
||||
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, ?> 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, ?> 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)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
package com.jessebrault.ssg.dsl.tagbuilder
|
||||
|
||||
interface TagBuilder {
|
||||
String create(String name)
|
||||
String create(String name, Map<String, ?> attributes)
|
||||
String create(String name, String body)
|
||||
String create(String name, Map<String, ?> attributes, String body)
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
package com.jessebrault.ssg.dsl.urlbuilder
|
||||
|
||||
import java.nio.file.Path
|
||||
|
||||
final class PathBasedUrlBuilder implements UrlBuilder {
|
||||
|
||||
private final String absolute
|
||||
private final String baseUrl
|
||||
private final Path fromDirectory
|
||||
|
||||
PathBasedUrlBuilder(String targetPath, String baseUrl) {
|
||||
this.absolute = baseUrl + '/' + targetPath
|
||||
this.baseUrl = baseUrl
|
||||
def fromFilePath = Path.of(targetPath)
|
||||
if (fromFilePath.parent) {
|
||||
this.fromDirectory = fromFilePath.parent
|
||||
} else {
|
||||
this.fromDirectory = Path.of('')
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
String getAbsolute() {
|
||||
this.absolute
|
||||
}
|
||||
|
||||
@Override
|
||||
String absolute(String to) {
|
||||
this.baseUrl + '/' + to
|
||||
}
|
||||
|
||||
@Override
|
||||
String relative(String to) {
|
||||
this.fromDirectory.relativize(Path.of(to)).toString()
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
package com.jessebrault.ssg.dsl.urlbuilder
|
||||
|
||||
interface UrlBuilder {
|
||||
String getAbsolute()
|
||||
String absolute(String to)
|
||||
String relative(String to)
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
package com.jessebrault.ssg.html
|
||||
|
||||
import com.jessebrault.ssg.task.AbstractTask
|
||||
import com.jessebrault.ssg.task.Task
|
||||
import com.jessebrault.ssg.util.Diagnostic
|
||||
import com.jessebrault.ssg.util.Result
|
||||
import groovy.transform.EqualsAndHashCode
|
||||
import groovy.transform.NullCheck
|
||||
|
||||
@NullCheck
|
||||
@EqualsAndHashCode
|
||||
abstract class AbstractHtmlTask extends AbstractTask implements HtmlTask {
|
||||
|
||||
final String path
|
||||
private final File buildDir
|
||||
|
||||
AbstractHtmlTask(String name, String path, File buildDir) {
|
||||
super(name)
|
||||
this.path = path
|
||||
this.buildDir = buildDir
|
||||
}
|
||||
|
||||
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 target = new File(this.buildDir, this.path)
|
||||
target.createParentDirectories()
|
||||
target.write(content)
|
||||
[]
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
String toString() {
|
||||
"AbstractHtmlTask(path: ${ this.path }, super: ${ super.toString() })"
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
package com.jessebrault.ssg.html
|
||||
|
||||
import com.jessebrault.ssg.task.Task
|
||||
|
||||
interface HtmlTask extends Task {
|
||||
String getPath()
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
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
|
||||
}
|
@ -0,0 +1,65 @@
|
||||
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.util.Result
|
||||
import groovy.transform.EqualsAndHashCode
|
||||
import groovy.transform.NullCheck
|
||||
|
||||
@NullCheck
|
||||
@EqualsAndHashCode
|
||||
final class ModelToHtmlTask<T> extends AbstractHtmlTask {
|
||||
|
||||
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 path,
|
||||
TaskSpec taskSpec,
|
||||
Model<T> model,
|
||||
Template template,
|
||||
Collection<Text> allTexts,
|
||||
Collection<Model<Object>> allModels,
|
||||
Collection<Part> allParts
|
||||
) {
|
||||
super("modelToHtml:${ path }", path, 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.path,
|
||||
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() })"
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
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
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,62 @@
|
||||
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.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 {
|
||||
|
||||
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 path,
|
||||
TaskSpec taskSpec,
|
||||
Page page,
|
||||
Collection<Text> allTexts,
|
||||
Collection<Model<Object>> allModels,
|
||||
Collection<Part> allParts
|
||||
) {
|
||||
super("pageToHtml:${ path }", path, 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.path,
|
||||
allTasks,
|
||||
this.allTexts,
|
||||
this.allModels,
|
||||
this.allParts,
|
||||
this.siteSpec,
|
||||
this.globals
|
||||
))
|
||||
}
|
||||
|
||||
@Override
|
||||
String toString() {
|
||||
"PageToHtml(${ this.page }, ${ this.allTexts }, ${ this.allParts }, ${ super.toString() })"
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
package com.jessebrault.ssg.html
|
||||
|
||||
import com.jessebrault.ssg.page.Page
|
||||
import com.jessebrault.ssg.provider.CollectionProvider
|
||||
import com.jessebrault.ssg.provider.CollectionProviders
|
||||
import com.jessebrault.ssg.task.AbstractRenderTaskFactory
|
||||
import com.jessebrault.ssg.task.Task
|
||||
import com.jessebrault.ssg.task.TaskSpec
|
||||
import com.jessebrault.ssg.util.Result
|
||||
|
||||
import static com.jessebrault.ssg.util.ExtensionUtil.stripExtension
|
||||
import static java.util.Objects.requireNonNull
|
||||
|
||||
final class PageToHtmlTaskFactory extends AbstractRenderTaskFactory {
|
||||
|
||||
CollectionProvider<Page> pagesProvider = CollectionProviders.getEmpty()
|
||||
|
||||
@Override
|
||||
Result<Collection<Task>> getTasks(TaskSpec taskSpec) {
|
||||
super.checkProviders()
|
||||
requireNonNull(this.pagesProvider)
|
||||
|
||||
def allTexts = this.allTextsProvider.provide()
|
||||
def allModels = this.allModelsProvider.provide()
|
||||
def allParts = this.allPartsProvider.provide()
|
||||
|
||||
final Collection<Task> tasks = this.pagesProvider.provide()
|
||||
.collect {
|
||||
new PageToHtmlTask(
|
||||
stripExtension(it.path) + '.html',
|
||||
taskSpec,
|
||||
it,
|
||||
allTexts,
|
||||
allModels,
|
||||
allParts
|
||||
)
|
||||
}
|
||||
Result.of(tasks)
|
||||
}
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
package com.jessebrault.ssg.html
|
||||
|
||||
import com.jessebrault.ssg.template.Template
|
||||
import com.jessebrault.ssg.text.Text
|
||||
import groovy.transform.EqualsAndHashCode
|
||||
import groovy.transform.NullCheck
|
||||
import groovy.transform.TupleConstructor
|
||||
|
||||
@TupleConstructor(defaults = false)
|
||||
@NullCheck(includeGenerated = true)
|
||||
@EqualsAndHashCode
|
||||
final class TextToHtmlSpec {
|
||||
final Text text
|
||||
final Template template
|
||||
final String path
|
||||
}
|
@ -0,0 +1,65 @@
|
||||
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.util.Result
|
||||
import groovy.transform.EqualsAndHashCode
|
||||
import groovy.transform.NullCheck
|
||||
|
||||
@NullCheck
|
||||
@EqualsAndHashCode(includeFields = true, callSuper = true)
|
||||
final class TextToHtmlTask extends AbstractHtmlTask {
|
||||
|
||||
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 path,
|
||||
TaskSpec taskSpec,
|
||||
Text text,
|
||||
Template template,
|
||||
Collection<Text> allTexts,
|
||||
Collection<Model<Object>> allModels,
|
||||
Collection<Part> allParts
|
||||
) {
|
||||
super("textToHtml:${ path }", path, 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.path,
|
||||
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() })"
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,48 @@
|
||||
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>> specProvider = CollectionProviders.getEmpty()
|
||||
|
||||
@Override
|
||||
Result<Collection<Task>> getTasks(TaskSpec taskSpec) {
|
||||
super.checkProviders()
|
||||
requireNonNull(this.specProvider)
|
||||
|
||||
def allTexts = this.allTextsProvider.provide()
|
||||
def allModels = this.allModelsProvider.provide()
|
||||
def allParts = this.allPartsProvider.provide()
|
||||
|
||||
Collection<Diagnostic> diagnostics = []
|
||||
|
||||
final Collection<Task> tasks = this.specProvider.provide()
|
||||
.findResults {
|
||||
if (it.hasDiagnostics()) {
|
||||
diagnostics.addAll(it.diagnostics)
|
||||
} else {
|
||||
def spec = it.get()
|
||||
new TextToHtmlTask(
|
||||
spec.path,
|
||||
taskSpec,
|
||||
spec.text,
|
||||
spec.template,
|
||||
allTexts,
|
||||
allModels,
|
||||
allParts
|
||||
)
|
||||
}
|
||||
}
|
||||
Result.of(diagnostics, tasks)
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
package com.jessebrault.ssg.model
|
||||
|
||||
import groovy.transform.EqualsAndHashCode
|
||||
import groovy.transform.NullCheck
|
||||
import groovy.transform.PackageScope
|
||||
import groovy.transform.TupleConstructor
|
||||
|
||||
@PackageScope
|
||||
@TupleConstructor(includeFields = true, defaults = false)
|
||||
@NullCheck(includeGenerated = true)
|
||||
@EqualsAndHashCode(includeFields = true)
|
||||
final class ClosureBasedModel<T> implements Model<T> {
|
||||
|
||||
final String name
|
||||
private final Closure<T> tClosure
|
||||
|
||||
@Override
|
||||
T get() {
|
||||
this.tClosure()
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
package com.jessebrault.ssg.model
|
||||
|
||||
interface Model<T> {
|
||||
String getName()
|
||||
T get()
|
||||
}
|
46
api/src/main/groovy/com/jessebrault/ssg/model/Models.groovy
Normal file
46
api/src/main/groovy/com/jessebrault/ssg/model/Models.groovy
Normal file
@ -0,0 +1,46 @@
|
||||
package com.jessebrault.ssg.model
|
||||
|
||||
import groovy.io.FileType
|
||||
import groovy.transform.stc.ClosureParams
|
||||
import groovy.transform.stc.FromString
|
||||
|
||||
import java.nio.file.Path
|
||||
|
||||
final class Models {
|
||||
|
||||
static <T> Model<T> of(String name, T t) {
|
||||
new SimpleModel<>(name, t)
|
||||
}
|
||||
|
||||
static <T> Model<T> from(String name, Closure<T> tClosure) {
|
||||
new ClosureBasedModel<>(name, tClosure)
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes a directory and iterates recursively through all files present in the directory and sub-directories,
|
||||
* supplying each File along with a String representing that File's path relative to the base Directory to the
|
||||
* given Closure, which then returns a Model containing T, all of which are collected and then returned together.
|
||||
*
|
||||
* @param directory The base directory in which to search for Files to process.
|
||||
* @param fileToModelClosure A Closure which receives two params: the File being processed,
|
||||
* and a String representing the path of that File relative to the base directory. Must return
|
||||
* a Model containing T.
|
||||
* @return A Collection of Models containing Ts.
|
||||
*/
|
||||
static <T> Collection<Model<T>> fromDirectory(
|
||||
File directory,
|
||||
@ClosureParams(value = FromString, options = ['java.io.File, java.lang.String'])
|
||||
Closure<Model<T>> fileToModelClosure
|
||||
) {
|
||||
final Collection<Model<T>> models = []
|
||||
def directoryPath = Path.of(directory.path)
|
||||
directory.eachFileRecurse(FileType.FILES) {
|
||||
def relativePath = directoryPath.relativize(Path.of(it.path)).toString()
|
||||
models << fileToModelClosure(it, relativePath)
|
||||
}
|
||||
models
|
||||
}
|
||||
|
||||
private Models() {}
|
||||
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
package com.jessebrault.ssg.model
|
||||
|
||||
import groovy.transform.EqualsAndHashCode
|
||||
import groovy.transform.NullCheck
|
||||
import groovy.transform.PackageScope
|
||||
import groovy.transform.TupleConstructor
|
||||
|
||||
@PackageScope
|
||||
@TupleConstructor(includeFields = true, defaults = false)
|
||||
@NullCheck(includeGenerated = true)
|
||||
@EqualsAndHashCode(includeFields = true)
|
||||
final class SimpleModel<T> implements Model<T> {
|
||||
|
||||
final String name
|
||||
private final T t
|
||||
|
||||
@Override
|
||||
T get() {
|
||||
this.t
|
||||
}
|
||||
|
||||
@Override
|
||||
String toString() {
|
||||
"SimpleModel(${ this.t })"
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
package com.jessebrault.ssg.page
|
||||
|
||||
import com.jessebrault.ssg.render.RenderContext
|
||||
import com.jessebrault.ssg.render.StandardGspRenderer
|
||||
import com.jessebrault.ssg.util.Diagnostic
|
||||
import com.jessebrault.ssg.util.Result
|
||||
import groovy.transform.EqualsAndHashCode
|
||||
import groovy.transform.NullCheck
|
||||
|
||||
@NullCheck
|
||||
@EqualsAndHashCode
|
||||
final class GspPageRenderer implements PageRenderer {
|
||||
|
||||
private final StandardGspRenderer gspRenderer = new StandardGspRenderer(this.class.classLoader)
|
||||
|
||||
@Override
|
||||
Result<String> render(
|
||||
Page specialPage,
|
||||
RenderContext context
|
||||
) {
|
||||
def diagnostics = []
|
||||
try {
|
||||
def result = this.gspRenderer.render(specialPage.text, context) {
|
||||
it.loggerName = "GspSpecialPage(${ specialPage.path })"
|
||||
it.onDiagnostics = diagnostics.&addAll
|
||||
return
|
||||
}
|
||||
Result.of(diagnostics, result.toString())
|
||||
} catch (Exception e) {
|
||||
Result.of(
|
||||
[*diagnostics, new Diagnostic(
|
||||
"An exception occurred while rendering specialPage ${ specialPage.path }:\n${ e }",
|
||||
e
|
||||
)],
|
||||
''
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
String toString() {
|
||||
"GspSpecialPageRenderer()"
|
||||
}
|
||||
|
||||
}
|
21
api/src/main/groovy/com/jessebrault/ssg/page/Page.groovy
Normal file
21
api/src/main/groovy/com/jessebrault/ssg/page/Page.groovy
Normal file
@ -0,0 +1,21 @@
|
||||
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 Page {
|
||||
|
||||
final String path
|
||||
final PageType type
|
||||
final String text
|
||||
|
||||
@Override
|
||||
String toString() {
|
||||
"Page(path: ${ this.path }, type: ${ this.type })"
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
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
|
||||
)
|
||||
|
||||
}
|
20
api/src/main/groovy/com/jessebrault/ssg/page/PageType.groovy
Normal file
20
api/src/main/groovy/com/jessebrault/ssg/page/PageType.groovy
Normal file
@ -0,0 +1,20 @@
|
||||
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 })"
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
package com.jessebrault.ssg.page
|
||||
|
||||
final class PageTypes {
|
||||
|
||||
static final PageType GSP = new PageType(['.gsp'], new GspPageRenderer())
|
||||
|
||||
private PageTypes() {}
|
||||
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
package com.jessebrault.ssg.page
|
||||
|
||||
import com.jessebrault.ssg.provider.CollectionProvider
|
||||
import com.jessebrault.ssg.provider.CollectionProviders
|
||||
import com.jessebrault.ssg.util.ExtensionUtil
|
||||
import com.jessebrault.ssg.util.PathUtil
|
||||
import org.slf4j.Logger
|
||||
import org.slf4j.LoggerFactory
|
||||
|
||||
final class PagesProviders {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(PagesProviders)
|
||||
|
||||
static CollectionProvider<Page> from(File pagesDirectory, Collection<PageType> pageTypes) {
|
||||
CollectionProviders.from(pagesDirectory) {
|
||||
def extension = ExtensionUtil.getExtension(it.path)
|
||||
def pageType = pageTypes.find { it.ids.contains(extension) }
|
||||
if (!pageType) {
|
||||
logger.warn('there is no PageType for file {}; skipping', it)
|
||||
null
|
||||
} else {
|
||||
new Page(PathUtil.relative(pagesDirectory.path, it.path), pageType, it.getText())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private PagesProviders() {}
|
||||
|
||||
}
|
@ -0,0 +1,56 @@
|
||||
package com.jessebrault.ssg.part
|
||||
|
||||
import com.jessebrault.ssg.render.StandardGspRenderer
|
||||
import com.jessebrault.ssg.util.Diagnostic
|
||||
import com.jessebrault.ssg.render.RenderContext
|
||||
import com.jessebrault.ssg.util.Result
|
||||
import com.jessebrault.ssg.text.Text
|
||||
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 = new StandardGspRenderer(this.class.classLoader)
|
||||
|
||||
@Override
|
||||
Result<String> render(
|
||||
Part part,
|
||||
Map<String, Object> binding,
|
||||
RenderContext context,
|
||||
@Nullable Text text
|
||||
) {
|
||||
requireNonNull(part)
|
||||
requireNonNull(binding)
|
||||
requireNonNull(context)
|
||||
def diagnostics = []
|
||||
try {
|
||||
def result = this.gspRenderer.render(part.text, context) {
|
||||
it.putCustom('binding', binding)
|
||||
it.loggerName = "GspPart(${ part.path })"
|
||||
it.onDiagnostics = diagnostics.&addAll
|
||||
if (text) {
|
||||
it.text = text
|
||||
}
|
||||
return
|
||||
}
|
||||
Result.of(diagnostics, result.toString())
|
||||
} catch (Exception e) {
|
||||
Result.of(
|
||||
[*diagnostics, new Diagnostic(
|
||||
"An exception occurred while rendering part ${ part.path }:\n${ e }",
|
||||
e
|
||||
)],
|
||||
''
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
String toString() {
|
||||
"GspPartRenderer()"
|
||||
}
|
||||
|
||||
}
|
21
api/src/main/groovy/com/jessebrault/ssg/part/Part.groovy
Normal file
21
api/src/main/groovy/com/jessebrault/ssg/part/Part.groovy
Normal file
@ -0,0 +1,21 @@
|
||||
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 })"
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
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
|
||||
)
|
||||
|
||||
}
|
20
api/src/main/groovy/com/jessebrault/ssg/part/PartType.groovy
Normal file
20
api/src/main/groovy/com/jessebrault/ssg/part/PartType.groovy
Normal file
@ -0,0 +1,20 @@
|
||||
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 })"
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
package com.jessebrault.ssg.part
|
||||
|
||||
final class PartTypes {
|
||||
|
||||
static final PartType GSP = new PartType(['.gsp'], new GspPartRenderer())
|
||||
|
||||
private PartTypes() {}
|
||||
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
package com.jessebrault.ssg.part
|
||||
|
||||
import com.jessebrault.ssg.provider.CollectionProvider
|
||||
import com.jessebrault.ssg.provider.CollectionProviders
|
||||
import com.jessebrault.ssg.util.ExtensionUtil
|
||||
import com.jessebrault.ssg.util.PathUtil
|
||||
import org.slf4j.Logger
|
||||
import org.slf4j.LoggerFactory
|
||||
|
||||
final class PartsProviders {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(PartsProviders)
|
||||
|
||||
static CollectionProvider<Part> of(File partsDir, Collection<PartType> partTypes) {
|
||||
CollectionProviders.from(partsDir) {
|
||||
def extension = ExtensionUtil.getExtension(it.path)
|
||||
def partType = partTypes.find { it.ids.contains(extension) }
|
||||
if (!partType) {
|
||||
logger.warn('there is no PartType for file {}; skipping', it)
|
||||
null
|
||||
} else {
|
||||
new Part(PathUtil.relative(partsDir.path, it.path), partType, it.getText())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private PartsProviders() {}
|
||||
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
package com.jessebrault.ssg.provider
|
||||
|
||||
abstract class AbstractCollectionProvider<T> implements CollectionProvider<T> {
|
||||
|
||||
static <T> CollectionProvider<T> concat(
|
||||
CollectionProvider<T> cp0,
|
||||
CollectionProvider<T> cp1
|
||||
) {
|
||||
ClosureBasedCollectionProvider.get {
|
||||
cp0.provide() + cp1.provide()
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
CollectionProvider<T> plus(Provider<T> other) {
|
||||
concat(this, other as CollectionProvider<T>)
|
||||
}
|
||||
|
||||
@Override
|
||||
CollectionProvider<T> plus(CollectionProvider<T> other) {
|
||||
concat(this, other)
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
package com.jessebrault.ssg.provider
|
||||
|
||||
abstract class AbstractProvider<T> implements Provider<T> {
|
||||
|
||||
static <T> CollectionProvider<T> concat(
|
||||
Provider<T> p0,
|
||||
Provider<T> p1
|
||||
) {
|
||||
ClosureBasedCollectionProvider.get {
|
||||
[p0.provide(), p1.provide()]
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
CollectionProvider<T> plus(Provider<T> other) {
|
||||
concat(this, other)
|
||||
}
|
||||
|
||||
@Override
|
||||
CollectionProvider<T> asType(Class<CollectionProvider> collectionProviderClass) {
|
||||
ClosureBasedCollectionProvider.get {
|
||||
[this.provide() as T]
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
package com.jessebrault.ssg.provider
|
||||
|
||||
import groovy.transform.EqualsAndHashCode
|
||||
import groovy.transform.NullCheck
|
||||
import groovy.transform.PackageScope
|
||||
import groovy.transform.TupleConstructor
|
||||
|
||||
@PackageScope
|
||||
@TupleConstructor(defaults = false, includeFields = true)
|
||||
@NullCheck(includeGenerated = true)
|
||||
@EqualsAndHashCode(includeFields = true)
|
||||
final class ClosureBasedCollectionProvider<T> extends AbstractCollectionProvider<T> {
|
||||
|
||||
static <T> CollectionProvider<T> get(Closure<Collection<T>> closure) {
|
||||
new ClosureBasedCollectionProvider<>(closure)
|
||||
}
|
||||
|
||||
private final Closure<Collection<T>> closure
|
||||
|
||||
@Override
|
||||
Collection<T> provide() {
|
||||
this.closure()
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
package com.jessebrault.ssg.provider
|
||||
|
||||
import groovy.transform.EqualsAndHashCode
|
||||
import groovy.transform.NullCheck
|
||||
import groovy.transform.PackageScope
|
||||
import groovy.transform.TupleConstructor
|
||||
|
||||
@PackageScope
|
||||
@TupleConstructor(includeFields = true, defaults = false)
|
||||
@NullCheck(includeGenerated = true)
|
||||
@EqualsAndHashCode(includeFields = true)
|
||||
final class ClosureBasedProvider<T> extends AbstractProvider<T> {
|
||||
|
||||
static <T> Provider<T> of(Closure<T> closure) {
|
||||
new ClosureBasedProvider<>(closure)
|
||||
}
|
||||
|
||||
private final Closure<T> closure
|
||||
|
||||
@Override
|
||||
T provide() {
|
||||
this.closure()
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
package com.jessebrault.ssg.provider
|
||||
|
||||
interface CollectionProvider<T> {
|
||||
Collection<T> provide()
|
||||
CollectionProvider<T> plus(Provider<T> other)
|
||||
CollectionProvider<T> plus(CollectionProvider<T> other)
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
package com.jessebrault.ssg.provider
|
||||
|
||||
import groovy.transform.stc.ClosureParams
|
||||
import groovy.transform.stc.FromString
|
||||
import org.jetbrains.annotations.Nullable
|
||||
|
||||
final class CollectionProviders {
|
||||
|
||||
static <T> CollectionProvider<T> getEmpty() {
|
||||
new SimpleCollectionProvider<>([])
|
||||
}
|
||||
|
||||
static <T> CollectionProvider<T> of(Collection<T> ts) {
|
||||
new SimpleCollectionProvider<T>(ts)
|
||||
}
|
||||
|
||||
static <T> CollectionProvider<T> from(Closure<Collection<T>> closure) {
|
||||
ClosureBasedCollectionProvider.get(closure)
|
||||
}
|
||||
|
||||
static <T> CollectionProvider<T> from(
|
||||
File dir,
|
||||
@ClosureParams(value = FromString, options = 'java.io.File')
|
||||
Closure<@Nullable T> fileToElementClosure
|
||||
) {
|
||||
new FileBasedCollectionProvider<>(dir, fileToElementClosure)
|
||||
}
|
||||
|
||||
private CollectionProviders() {}
|
||||
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
package com.jessebrault.ssg.provider
|
||||
|
||||
import groovy.io.FileType
|
||||
import groovy.transform.EqualsAndHashCode
|
||||
import groovy.transform.NullCheck
|
||||
import groovy.transform.PackageScope
|
||||
import groovy.transform.stc.ClosureParams
|
||||
import groovy.transform.stc.FromString
|
||||
import org.jetbrains.annotations.Nullable
|
||||
import org.slf4j.Logger
|
||||
import org.slf4j.LoggerFactory
|
||||
|
||||
@PackageScope
|
||||
@NullCheck
|
||||
@EqualsAndHashCode(includeFields = true)
|
||||
final class FileBasedCollectionProvider<T> extends AbstractCollectionProvider<T> {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(FileBasedCollectionProvider)
|
||||
|
||||
private final File dir
|
||||
private final Closure<@Nullable T> fileToElementClosure
|
||||
|
||||
FileBasedCollectionProvider(
|
||||
File dir,
|
||||
@ClosureParams(value = FromString, options = 'java.io.File')
|
||||
Closure<@Nullable T> fileToElementClosure
|
||||
) {
|
||||
this.dir = dir
|
||||
this.fileToElementClosure = fileToElementClosure
|
||||
}
|
||||
|
||||
@Override
|
||||
Collection<T> provide() {
|
||||
if (!this.dir.isDirectory()) {
|
||||
logger.error('{} does not exist or is not a directory; returning empty collection', this.dir)
|
||||
[]
|
||||
} else {
|
||||
def ts = []
|
||||
this.dir.eachFileRecurse(FileType.FILES) {
|
||||
def t = this.fileToElementClosure(it)
|
||||
if (t) {
|
||||
ts << t
|
||||
}
|
||||
}
|
||||
ts
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
package com.jessebrault.ssg.provider
|
||||
|
||||
interface Provider<T> {
|
||||
T provide()
|
||||
CollectionProvider<T> plus(Provider<T> other)
|
||||
CollectionProvider<T> asType(Class<CollectionProvider> collectionProviderClass)
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
package com.jessebrault.ssg.provider
|
||||
|
||||
import org.codehaus.groovy.runtime.InvokerHelper
|
||||
|
||||
final class Providers {
|
||||
|
||||
static <T> Provider<T> of(T t) {
|
||||
new SimpleProvider<>(t)
|
||||
}
|
||||
|
||||
static <T> Provider<T> from(Closure<T> closure) {
|
||||
ClosureBasedProvider.of(closure)
|
||||
}
|
||||
|
||||
static <T> CollectionProvider<T> concat(Provider<T> ...providers) {
|
||||
concat(List.of(providers))
|
||||
}
|
||||
|
||||
static <T> CollectionProvider<T> concat(Collection<Provider<T>> providers) {
|
||||
providers.inject(CollectionProviders.<T>getEmpty()) { acc, val ->
|
||||
acc + val
|
||||
}
|
||||
}
|
||||
|
||||
private Providers() {}
|
||||
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
package com.jessebrault.ssg.provider
|
||||
|
||||
import groovy.transform.EqualsAndHashCode
|
||||
import groovy.transform.NullCheck
|
||||
import groovy.transform.PackageScope
|
||||
import groovy.transform.TupleConstructor
|
||||
|
||||
@PackageScope
|
||||
@TupleConstructor(defaults = false, includeFields = true)
|
||||
@NullCheck(includeGenerated = true)
|
||||
@EqualsAndHashCode(includeFields = true)
|
||||
final class SimpleCollectionProvider<T> extends AbstractCollectionProvider<T> {
|
||||
|
||||
private final Collection<T> ts
|
||||
|
||||
@Override
|
||||
Collection<T> provide() {
|
||||
this.ts
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
package com.jessebrault.ssg.provider
|
||||
|
||||
import groovy.transform.EqualsAndHashCode
|
||||
import groovy.transform.NullCheck
|
||||
import groovy.transform.PackageScope
|
||||
import groovy.transform.TupleConstructor
|
||||
|
||||
@PackageScope
|
||||
@TupleConstructor(defaults = false, includeFields = true)
|
||||
@NullCheck(includeGenerated = true)
|
||||
@EqualsAndHashCode(includeFields = true)
|
||||
final class SimpleProvider<T> extends AbstractProvider<T> {
|
||||
|
||||
private final T t
|
||||
|
||||
@Override
|
||||
T provide() {
|
||||
this.t
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
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
|
||||
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
package com.jessebrault.ssg.render
|
||||
|
||||
import com.jessebrault.ssg.dsl.StandardDslMap
|
||||
import groovy.text.GStringTemplateEngine
|
||||
import groovy.text.TemplateEngine
|
||||
import org.codehaus.groovy.control.CompilerConfiguration
|
||||
|
||||
final class StandardGspRenderer {
|
||||
|
||||
private final TemplateEngine engine
|
||||
|
||||
StandardGspRenderer(ClassLoader parentClassLoader) {
|
||||
def cc = new CompilerConfiguration() // TODO: investigate if this makes any difference on the ultimate template
|
||||
def gcl = new GroovyClassLoader(parentClassLoader, cc)
|
||||
this.engine = new GStringTemplateEngine(gcl)
|
||||
}
|
||||
|
||||
String render(
|
||||
String template,
|
||||
RenderContext context,
|
||||
@DelegatesTo(value = StandardDslMap.Builder, strategy = Closure.DELEGATE_FIRST)
|
||||
Closure<Void> dslMapBuilderClosure
|
||||
) {
|
||||
this.engine.createTemplate(template).make(StandardDslMap.get(context, dslMapBuilderClosure)).toString()
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
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)
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
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 })"
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
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)
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
package com.jessebrault.ssg.task
|
||||
|
||||
trait RenderingTaskFactory {
|
||||
|
||||
}
|
8
api/src/main/groovy/com/jessebrault/ssg/task/Task.groovy
Normal file
8
api/src/main/groovy/com/jessebrault/ssg/task/Task.groovy
Normal file
@ -0,0 +1,8 @@
|
||||
package com.jessebrault.ssg.task
|
||||
|
||||
import com.jessebrault.ssg.util.Diagnostic
|
||||
|
||||
interface Task {
|
||||
String getName()
|
||||
Collection<Diagnostic> execute(Collection<Task> allTasks)
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
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() {}
|
||||
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
package com.jessebrault.ssg.task
|
||||
|
||||
import com.jessebrault.ssg.util.Result
|
||||
|
||||
interface TaskFactory {
|
||||
Result<Collection<Task>> getTasks(TaskSpec taskSpec)
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
package com.jessebrault.ssg.task
|
||||
|
||||
import groovy.transform.EqualsAndHashCode
|
||||
import groovy.transform.NullCheck
|
||||
import groovy.transform.TupleConstructor
|
||||
|
||||
import java.util.function.Supplier
|
||||
|
||||
@TupleConstructor(defaults = false)
|
||||
@NullCheck(includeGenerated = true)
|
||||
@EqualsAndHashCode
|
||||
final class TaskFactorySpec {
|
||||
|
||||
static TaskFactorySpec concat(TaskFactorySpec spec0, TaskFactorySpec spec1) {
|
||||
if (spec0.supplier != spec1.supplier) {
|
||||
throw new IllegalArgumentException("suppliers must be equal!")
|
||||
}
|
||||
new TaskFactorySpec(spec0.supplier, spec0.configureClosures + spec1.configureClosures)
|
||||
}
|
||||
|
||||
final Supplier<TaskFactory> supplier
|
||||
final Collection<Closure<Void>> configureClosures
|
||||
|
||||
TaskFactorySpec plus(TaskFactorySpec other) {
|
||||
concat(this, other)
|
||||
}
|
||||
|
||||
}
|
22
api/src/main/groovy/com/jessebrault/ssg/task/TaskSpec.groovy
Normal file
22
api/src/main/groovy/com/jessebrault/ssg/task/TaskSpec.groovy
Normal file
@ -0,0 +1,22 @@
|
||||
package com.jessebrault.ssg.task
|
||||
|
||||
import com.jessebrault.ssg.SiteSpec
|
||||
import groovy.transform.EqualsAndHashCode
|
||||
import groovy.transform.NullCheck
|
||||
import groovy.transform.TupleConstructor
|
||||
|
||||
@TupleConstructor(defaults = false)
|
||||
@NullCheck(includeGenerated = true)
|
||||
@EqualsAndHashCode
|
||||
final class TaskSpec {
|
||||
|
||||
static TaskSpec getEmpty() {
|
||||
new TaskSpec('', new File(''), SiteSpec.getBlank(), [:])
|
||||
}
|
||||
|
||||
final String buildName
|
||||
final File outputDir
|
||||
final SiteSpec siteSpec
|
||||
final Map<String, Object> globals
|
||||
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
package com.jessebrault.ssg.task.collector
|
||||
|
||||
import com.jessebrault.ssg.provider.Provider
|
||||
import com.jessebrault.ssg.task.TaskFactory
|
||||
import groovy.io.FileType
|
||||
import groovy.transform.NullCheck
|
||||
import groovy.transform.TupleConstructor
|
||||
|
||||
@TupleConstructor(defaults = false, includeFields = true)
|
||||
@NullCheck(includeGenerated = true)
|
||||
final class GroovyFileTaskFactoryCollector implements TaskFactoryCollector {
|
||||
|
||||
private final GroovyClassLoader groovyClassLoader
|
||||
private final Collection<Provider<File>> factoryDirectoryProviders
|
||||
|
||||
@Override
|
||||
Collection<TaskFactory> getAllFactories() {
|
||||
Collection<TaskFactory> factories = []
|
||||
|
||||
def pluginDirectories = this.factoryDirectoryProviders.collect { it.provide() }
|
||||
pluginDirectories.each {
|
||||
it.eachFileRecurse(FileType.FILES) {
|
||||
def cl = this.groovyClassLoader.parseClass(it)
|
||||
if (TaskFactory.isAssignableFrom(cl)) {
|
||||
def constructor = cl.getDeclaredConstructor()
|
||||
def factory = constructor.newInstance() as TaskFactory
|
||||
factories << factory
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
factories
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
package com.jessebrault.ssg.task.collector
|
||||
|
||||
import com.jessebrault.ssg.task.TaskFactory
|
||||
import groovy.transform.NullCheck
|
||||
import groovy.transform.TupleConstructor
|
||||
|
||||
@TupleConstructor(includeFields = true, defaults = false)
|
||||
@NullCheck(includeGenerated = true)
|
||||
final class ServiceTaskFactoryCollector implements TaskFactoryCollector {
|
||||
|
||||
private final ClassLoader classLoader
|
||||
|
||||
@Override
|
||||
Collection<TaskFactory> getAllFactories() {
|
||||
ServiceLoader.load(TaskFactory, this.classLoader).asList()
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
package com.jessebrault.ssg.task.collector
|
||||
|
||||
import com.jessebrault.ssg.task.TaskFactory
|
||||
|
||||
interface TaskFactoryCollector {
|
||||
Collection<TaskFactory> getAllFactories()
|
||||
}
|
@ -0,0 +1,48 @@
|
||||
package com.jessebrault.ssg.template
|
||||
|
||||
import com.jessebrault.ssg.render.StandardGspRenderer
|
||||
import com.jessebrault.ssg.util.Diagnostic
|
||||
import com.jessebrault.ssg.render.RenderContext
|
||||
import com.jessebrault.ssg.util.Result
|
||||
import com.jessebrault.ssg.text.Text
|
||||
import groovy.transform.EqualsAndHashCode
|
||||
import groovy.transform.NullCheck
|
||||
|
||||
@NullCheck
|
||||
@EqualsAndHashCode
|
||||
final class GspTemplateRenderer implements TemplateRenderer {
|
||||
|
||||
private final StandardGspRenderer gspRenderer = new StandardGspRenderer(this.class.classLoader)
|
||||
|
||||
@Override
|
||||
Result<String> render(
|
||||
Template template,
|
||||
Text text,
|
||||
RenderContext context
|
||||
) {
|
||||
def diagnostics = []
|
||||
try {
|
||||
def result = this.gspRenderer.render(template.text, context) {
|
||||
it.loggerName = "GspTemplate(${ template.path })"
|
||||
it.onDiagnostics = diagnostics.&addAll
|
||||
it.text = text
|
||||
return
|
||||
}
|
||||
Result.of(diagnostics, result)
|
||||
} catch (Exception e) {
|
||||
Result.of(
|
||||
[*diagnostics, new Diagnostic(
|
||||
"An exception occurred while rendering Template ${ template.path }:\n${ e }",
|
||||
e
|
||||
)],
|
||||
''
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
String toString() {
|
||||
"GspTemplateRenderer()"
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
package com.jessebrault.ssg.template
|
||||
|
||||
import groovy.transform.EqualsAndHashCode
|
||||
import groovy.transform.NullCheck
|
||||
import groovy.transform.TupleConstructor
|
||||
|
||||
@TupleConstructor(defaults = false)
|
||||
@NullCheck(includeGenerated = true)
|
||||
@EqualsAndHashCode
|
||||
final class Template {
|
||||
|
||||
String path
|
||||
TemplateType type
|
||||
String text
|
||||
|
||||
@Override
|
||||
String toString() {
|
||||
"Template(path: ${ this.path }, type: ${ this.type })"
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
package com.jessebrault.ssg.template
|
||||
|
||||
import com.jessebrault.ssg.render.RenderContext
|
||||
import com.jessebrault.ssg.util.Result
|
||||
import com.jessebrault.ssg.text.Text
|
||||
|
||||
interface TemplateRenderer {
|
||||
|
||||
Result<String> render(
|
||||
Template template,
|
||||
Text text,
|
||||
RenderContext context
|
||||
)
|
||||
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
package com.jessebrault.ssg.template
|
||||
|
||||
import groovy.transform.EqualsAndHashCode
|
||||
import groovy.transform.NullCheck
|
||||
import groovy.transform.TupleConstructor
|
||||
|
||||
@TupleConstructor(defaults = false)
|
||||
@NullCheck(includeGenerated = true)
|
||||
@EqualsAndHashCode
|
||||
final class TemplateType {
|
||||
|
||||
Collection<String> ids
|
||||
TemplateRenderer renderer
|
||||
|
||||
@Override
|
||||
String toString() {
|
||||
"TemplateType(ids: ${ this.ids }, renderer: ${ this.renderer })"
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
package com.jessebrault.ssg.template
|
||||
|
||||
final class TemplateTypes {
|
||||
|
||||
static final TemplateType GSP = new TemplateType(['.gsp'], new GspTemplateRenderer())
|
||||
|
||||
private TemplateTypes() {}
|
||||
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
package com.jessebrault.ssg.template
|
||||
|
||||
import com.jessebrault.ssg.provider.CollectionProvider
|
||||
import com.jessebrault.ssg.provider.CollectionProviders
|
||||
import com.jessebrault.ssg.util.ExtensionUtil
|
||||
import com.jessebrault.ssg.util.PathUtil
|
||||
import org.slf4j.Logger
|
||||
import org.slf4j.LoggerFactory
|
||||
|
||||
final class TemplatesProviders {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(TemplatesProviders)
|
||||
|
||||
static CollectionProvider<Template> from(File templatesDir, Collection<TemplateType> templateTypes) {
|
||||
CollectionProviders.from(templatesDir) {
|
||||
def extension = ExtensionUtil.getExtension(it.path)
|
||||
def templateType = templateTypes.find { it.ids.contains(extension) }
|
||||
if (!templateType) {
|
||||
logger.warn('there is no TemplateType for file {}; skipping', it)
|
||||
null
|
||||
} else {
|
||||
new Template(PathUtil.relative(templatesDir.path, it.path), templateType, it.getText())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private TemplatesProviders() {}
|
||||
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
package com.jessebrault.ssg.text
|
||||
|
||||
import com.jessebrault.ssg.util.Result
|
||||
|
||||
interface ExcerptGetter {
|
||||
Result<String> getExcerpt(Text text, int limit)
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
package com.jessebrault.ssg.text
|
||||
|
||||
import groovy.transform.EqualsAndHashCode
|
||||
import groovy.transform.NullCheck
|
||||
import groovy.transform.TupleConstructor
|
||||
import org.slf4j.Logger
|
||||
import org.slf4j.LoggerFactory
|
||||
|
||||
@TupleConstructor(includeFields = true, defaults = false)
|
||||
@NullCheck(includeGenerated = true)
|
||||
@EqualsAndHashCode(includeFields = true)
|
||||
final class FrontMatter {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(FrontMatter)
|
||||
|
||||
private final Text text
|
||||
private final Map<String, List<String>> data
|
||||
|
||||
String get(String key) {
|
||||
if (this.data[key] != null) {
|
||||
this.data[key][0]
|
||||
} else {
|
||||
logger.warn('in {} no entry for key {} in frontMatter, returning empty string', this.text, key)
|
||||
''
|
||||
}
|
||||
}
|
||||
|
||||
String getAt(String key) {
|
||||
this.get(key)
|
||||
}
|
||||
|
||||
List<String> getList(String key) {
|
||||
if (data[key] != null) {
|
||||
data[key]
|
||||
} else {
|
||||
logger.warn('in {} no entry for key {} in frontMatter, returning empty list: {}', this.text, key)
|
||||
[]
|
||||
}
|
||||
}
|
||||
|
||||
Optional<String> find(String key) {
|
||||
Optional.ofNullable(this.data[key]?[0])
|
||||
}
|
||||
|
||||
Optional<List<String>> findList(String key) {
|
||||
Optional.ofNullable(this.data[key])
|
||||
}
|
||||
|
||||
@Override
|
||||
String toString() {
|
||||
"FrontMatter(text: ${ this.text }, data: ${ this.data })"
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
package com.jessebrault.ssg.text
|
||||
|
||||
import com.jessebrault.ssg.util.Result
|
||||
|
||||
interface FrontMatterGetter {
|
||||
Result<FrontMatter> get(Text text)
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
package com.jessebrault.ssg.text
|
||||
|
||||
import com.jessebrault.ssg.util.Diagnostic
|
||||
import com.jessebrault.ssg.util.Result
|
||||
import org.commonmark.ext.front.matter.YamlFrontMatterExtension
|
||||
import org.commonmark.node.AbstractVisitor
|
||||
import org.commonmark.parser.Parser
|
||||
|
||||
final class MarkdownExcerptGetter implements ExcerptGetter {
|
||||
|
||||
private static class ExcerptVisitor extends AbstractVisitor {
|
||||
|
||||
final int limit
|
||||
List<String> words = []
|
||||
|
||||
ExcerptVisitor(int limit) {
|
||||
this.limit = limit
|
||||
}
|
||||
|
||||
@Override
|
||||
void visit(org.commonmark.node.Text text) {
|
||||
if (this.words.size() <= limit) {
|
||||
def textWords = text.literal.split('\\s+').toList()
|
||||
def taken = textWords.take(this.limit - this.words.size())
|
||||
this.words.addAll(taken)
|
||||
}
|
||||
}
|
||||
|
||||
String getResult() {
|
||||
this.words.take(this.limit).join(' ')
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static final Parser parser = Parser.builder()
|
||||
.extensions([YamlFrontMatterExtension.create()])
|
||||
.build()
|
||||
|
||||
@Override
|
||||
Result<String> getExcerpt(Text text, int limit) {
|
||||
try {
|
||||
def node = parser.parse(text.text)
|
||||
def visitor = new ExcerptVisitor(limit)
|
||||
node.accept(visitor)
|
||||
Result.of(visitor.result)
|
||||
} catch (Exception e) {
|
||||
def diagnostic = new Diagnostic(
|
||||
"There was an exception while getting the excerpt for ${ text }:\n${ e }",
|
||||
e
|
||||
)
|
||||
Result.of([diagnostic], '')
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
package com.jessebrault.ssg.text
|
||||
|
||||
import com.jessebrault.ssg.util.Diagnostic
|
||||
import com.jessebrault.ssg.util.Result
|
||||
import groovy.transform.EqualsAndHashCode
|
||||
import groovy.transform.NullCheck
|
||||
import org.commonmark.ext.front.matter.YamlFrontMatterExtension
|
||||
import org.commonmark.ext.front.matter.YamlFrontMatterVisitor
|
||||
import org.commonmark.parser.Parser
|
||||
|
||||
@NullCheck
|
||||
@EqualsAndHashCode
|
||||
final class MarkdownFrontMatterGetter implements FrontMatterGetter {
|
||||
|
||||
private static final Parser parser = Parser.builder()
|
||||
.extensions([YamlFrontMatterExtension.create()])
|
||||
.build()
|
||||
|
||||
@Override
|
||||
Result<FrontMatter> get(Text text) {
|
||||
try {
|
||||
def node = parser.parse(text.text)
|
||||
def v = new YamlFrontMatterVisitor()
|
||||
node.accept(v)
|
||||
Result.of(new FrontMatter(text, v.data))
|
||||
} catch (Exception e) {
|
||||
Result.of(
|
||||
[new Diagnostic(
|
||||
"An exception occured while parsing frontMatter for ${ text.path }:\n${ e }",
|
||||
e
|
||||
)],
|
||||
new FrontMatter(text, [:])
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
String toString() {
|
||||
"MarkdownFrontMatterGetter()"
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
package com.jessebrault.ssg.text
|
||||
|
||||
import com.jessebrault.ssg.util.Diagnostic
|
||||
import com.jessebrault.ssg.util.Result
|
||||
import groovy.transform.EqualsAndHashCode
|
||||
import groovy.transform.NullCheck
|
||||
import org.commonmark.ext.front.matter.YamlFrontMatterExtension
|
||||
import org.commonmark.parser.Parser
|
||||
import org.commonmark.renderer.html.HtmlRenderer
|
||||
|
||||
@NullCheck
|
||||
@EqualsAndHashCode
|
||||
final class MarkdownTextRenderer implements TextRenderer {
|
||||
|
||||
private static final Parser parser = Parser.builder()
|
||||
.extensions([YamlFrontMatterExtension.create()])
|
||||
.build()
|
||||
private static final HtmlRenderer htmlRenderer = HtmlRenderer.builder().build()
|
||||
|
||||
@Override
|
||||
Result<String> render(Text text) {
|
||||
try {
|
||||
Result.of(htmlRenderer.render(parser.parse(text.text)))
|
||||
} catch (Exception e) {
|
||||
Result.of(
|
||||
[new Diagnostic("There was an exception while rendering ${ text.path }:\n${ e }", e)],
|
||||
''
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
String toString() {
|
||||
"MarkdownTextRenderer()"
|
||||
}
|
||||
|
||||
}
|
21
api/src/main/groovy/com/jessebrault/ssg/text/Text.groovy
Normal file
21
api/src/main/groovy/com/jessebrault/ssg/text/Text.groovy
Normal file
@ -0,0 +1,21 @@
|
||||
package com.jessebrault.ssg.text
|
||||
|
||||
import groovy.transform.EqualsAndHashCode
|
||||
import groovy.transform.NullCheck
|
||||
import groovy.transform.TupleConstructor
|
||||
|
||||
@TupleConstructor(defaults = false)
|
||||
@NullCheck(includeGenerated = true)
|
||||
@EqualsAndHashCode
|
||||
final class Text {
|
||||
|
||||
final String path
|
||||
final TextType type
|
||||
final String text
|
||||
|
||||
@Override
|
||||
String toString() {
|
||||
"Text(path: ${ this.path }, type: ${ this.type })"
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
package com.jessebrault.ssg.text
|
||||
|
||||
import com.jessebrault.ssg.util.Result
|
||||
|
||||
interface TextRenderer {
|
||||
Result<String> render(Text text)
|
||||
}
|
22
api/src/main/groovy/com/jessebrault/ssg/text/TextType.groovy
Normal file
22
api/src/main/groovy/com/jessebrault/ssg/text/TextType.groovy
Normal file
@ -0,0 +1,22 @@
|
||||
package com.jessebrault.ssg.text
|
||||
|
||||
import groovy.transform.EqualsAndHashCode
|
||||
import groovy.transform.NullCheck
|
||||
import groovy.transform.TupleConstructor
|
||||
|
||||
@TupleConstructor(defaults = false)
|
||||
@NullCheck(includeGenerated = true)
|
||||
@EqualsAndHashCode
|
||||
final class TextType {
|
||||
|
||||
final Collection<String> ids
|
||||
final TextRenderer renderer
|
||||
final FrontMatterGetter frontMatterGetter
|
||||
final ExcerptGetter excerptGetter
|
||||
|
||||
@Override
|
||||
String toString() {
|
||||
"TextType(ids: ${ this.ids })"
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
package com.jessebrault.ssg.text
|
||||
|
||||
final class TextTypes {
|
||||
|
||||
static final MARKDOWN = new TextType(
|
||||
['.md'],
|
||||
new MarkdownTextRenderer(),
|
||||
new MarkdownFrontMatterGetter(),
|
||||
new MarkdownExcerptGetter()
|
||||
)
|
||||
|
||||
private TextTypes() {}
|
||||
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user