Added specialPages and some better error handling.
This commit is contained in:
parent
b293bb856b
commit
e6b603fd69
@ -3,6 +3,9 @@ package com.jessebrault.ssg
|
|||||||
import com.jessebrault.ssg.part.GspPartRenderer
|
import com.jessebrault.ssg.part.GspPartRenderer
|
||||||
import com.jessebrault.ssg.part.PartFilePartsProvider
|
import com.jessebrault.ssg.part.PartFilePartsProvider
|
||||||
import com.jessebrault.ssg.part.PartType
|
import com.jessebrault.ssg.part.PartType
|
||||||
|
import com.jessebrault.ssg.specialpage.GspSpecialPageRenderer
|
||||||
|
import com.jessebrault.ssg.specialpage.SpecialPageFileSpecialPagesProvider
|
||||||
|
import com.jessebrault.ssg.specialpage.SpecialPageType
|
||||||
import com.jessebrault.ssg.template.GspTemplateRenderer
|
import com.jessebrault.ssg.template.GspTemplateRenderer
|
||||||
import com.jessebrault.ssg.template.TemplateType
|
import com.jessebrault.ssg.template.TemplateType
|
||||||
import com.jessebrault.ssg.template.TemplateFileTemplatesProvider
|
import com.jessebrault.ssg.template.TemplateFileTemplatesProvider
|
||||||
@ -17,19 +20,23 @@ class StaticSiteGeneratorCli {
|
|||||||
def markdownText = new TextType(['.md'], new MarkdownTextRenderer(), new MarkdownFrontMatterGetter())
|
def markdownText = new TextType(['.md'], new MarkdownTextRenderer(), new MarkdownFrontMatterGetter())
|
||||||
def gspTemplate = new TemplateType(['.gsp'], new GspTemplateRenderer())
|
def gspTemplate = new TemplateType(['.gsp'], new GspTemplateRenderer())
|
||||||
def gspPart = new PartType(['.gsp'], new GspPartRenderer())
|
def gspPart = new PartType(['.gsp'], new GspPartRenderer())
|
||||||
|
def gspSpecialPage = new SpecialPageType(['.gsp'], new GspSpecialPageRenderer())
|
||||||
|
|
||||||
def config = new Config(
|
def config = new Config(
|
||||||
textTypes: [markdownText],
|
textTypes: [markdownText],
|
||||||
templateTypes: [gspTemplate],
|
templateTypes: [gspTemplate],
|
||||||
partTypes: [gspPart],
|
partTypes: [gspPart],
|
||||||
|
specialPageTypes: [gspSpecialPage],
|
||||||
|
|
||||||
textsDir: new File('texts'),
|
textsDir: new File('texts'),
|
||||||
templatesDir: new File('templates'),
|
templatesDir: new File('templates'),
|
||||||
partsDir: new File('parts'),
|
partsDir: new File('parts'),
|
||||||
|
specialPagesDir: new File('specialPages'),
|
||||||
|
|
||||||
textsProviderGetter: { Config config -> new TextFileTextsProvider(config.textTypes, config.textsDir) },
|
textsProviderGetter: { Config config -> new TextFileTextsProvider(config.textTypes, config.textsDir) },
|
||||||
templatesProviderGetter: { Config config -> new TemplateFileTemplatesProvider(config.templateTypes, config.templatesDir) },
|
templatesProviderGetter: { Config config -> new TemplateFileTemplatesProvider(config.templateTypes, config.templatesDir) },
|
||||||
partsProviderGetter: { Config config -> new PartFilePartsProvider(config.partTypes, config.partsDir) }
|
partsProviderGetter: { Config config -> new PartFilePartsProvider(config.partTypes, config.partsDir) },
|
||||||
|
specialPagesProviderGetter: { Config config -> new SpecialPageFileSpecialPagesProvider(config.specialPageTypes, config.specialPagesDir) }
|
||||||
)
|
)
|
||||||
|
|
||||||
def ssg = new SimpleStaticSiteGenerator(config)
|
def ssg = new SimpleStaticSiteGenerator(config)
|
||||||
|
@ -2,6 +2,8 @@ package com.jessebrault.ssg
|
|||||||
|
|
||||||
import com.jessebrault.ssg.part.PartType
|
import com.jessebrault.ssg.part.PartType
|
||||||
import com.jessebrault.ssg.part.PartsProvider
|
import com.jessebrault.ssg.part.PartsProvider
|
||||||
|
import com.jessebrault.ssg.specialpage.SpecialPageType
|
||||||
|
import com.jessebrault.ssg.specialpage.SpecialPagesProvider
|
||||||
import com.jessebrault.ssg.template.TemplateType
|
import com.jessebrault.ssg.template.TemplateType
|
||||||
import com.jessebrault.ssg.template.TemplatesProvider
|
import com.jessebrault.ssg.template.TemplatesProvider
|
||||||
import com.jessebrault.ssg.text.TextType
|
import com.jessebrault.ssg.text.TextType
|
||||||
@ -19,12 +21,15 @@ class Config {
|
|||||||
Collection<TextType> textTypes
|
Collection<TextType> textTypes
|
||||||
Collection<TemplateType> templateTypes
|
Collection<TemplateType> templateTypes
|
||||||
Collection<PartType> partTypes
|
Collection<PartType> partTypes
|
||||||
|
Collection<SpecialPageType> specialPageTypes
|
||||||
|
|
||||||
File textsDir
|
File textsDir
|
||||||
File templatesDir
|
File templatesDir
|
||||||
File partsDir
|
File partsDir
|
||||||
|
File specialPagesDir
|
||||||
|
|
||||||
Function<Config, TextsProvider> textsProviderGetter
|
Function<Config, TextsProvider> textsProviderGetter
|
||||||
Function<Config, TemplatesProvider> templatesProviderGetter
|
Function<Config, TemplatesProvider> templatesProviderGetter
|
||||||
Function<Config, PartsProvider> partsProviderGetter
|
Function<Config, PartsProvider> partsProviderGetter
|
||||||
|
Function<Config, SpecialPagesProvider> specialPagesProviderGetter
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package com.jessebrault.ssg
|
package com.jessebrault.ssg
|
||||||
|
|
||||||
import com.jessebrault.ssg.part.PartsProvider
|
import com.jessebrault.ssg.part.PartsProvider
|
||||||
|
import com.jessebrault.ssg.specialpage.SpecialPagesProvider
|
||||||
import com.jessebrault.ssg.template.TemplatesProvider
|
import com.jessebrault.ssg.template.TemplatesProvider
|
||||||
import com.jessebrault.ssg.text.TextsProvider
|
import com.jessebrault.ssg.text.TextsProvider
|
||||||
import org.slf4j.Logger
|
import org.slf4j.Logger
|
||||||
@ -18,24 +19,41 @@ class SimpleStaticSiteGenerator implements StaticSiteGenerator {
|
|||||||
private final TextsProvider textsProvider
|
private final TextsProvider textsProvider
|
||||||
private final TemplatesProvider templatesProvider
|
private final TemplatesProvider templatesProvider
|
||||||
private final PartsProvider partsProvider
|
private final PartsProvider partsProvider
|
||||||
|
private final SpecialPagesProvider specialPagesProvider
|
||||||
|
|
||||||
SimpleStaticSiteGenerator(Config config) {
|
SimpleStaticSiteGenerator(Config config) {
|
||||||
this.config = config
|
this.config = config
|
||||||
this.textsProvider = config.textsProviderGetter.apply(config)
|
this.textsProvider = config.textsProviderGetter.apply(config)
|
||||||
this.templatesProvider = config.templatesProviderGetter.apply(config)
|
this.templatesProvider = config.templatesProviderGetter.apply(config)
|
||||||
this.partsProvider = config.partsProviderGetter.apply(config)
|
this.partsProvider = config.partsProviderGetter.apply(config)
|
||||||
|
this.specialPagesProvider = config.specialPagesProviderGetter.apply(config)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
void generate(File buildDir) {
|
void generate(File buildDir) {
|
||||||
logger.trace(enter, 'buildDir: {}', buildDir)
|
logger.trace(enter, 'buildDir: {}', buildDir)
|
||||||
|
|
||||||
// Get all texts, templates, and parts
|
// Get all texts, templates, parts, and specialPages
|
||||||
def texts = this.textsProvider.getTextFiles()
|
def texts = this.textsProvider.getTextFiles()
|
||||||
def templates = this.templatesProvider.getTemplates()
|
def templates = this.templatesProvider.getTemplates()
|
||||||
def parts = this.partsProvider.getParts()
|
def parts = this.partsProvider.getParts()
|
||||||
|
def specialPages = this.specialPagesProvider.getSpecialPages()
|
||||||
|
|
||||||
logger.debug('texts: {}, templates: {}, parts: {}', texts, templates, parts)
|
logger.debug('\n\ttexts: {}\n\ttemplates: {}\n\tparts: {}\n\tspecialPages: {}', texts, templates, parts, specialPages)
|
||||||
|
|
||||||
|
// Define output function
|
||||||
|
def outputPage = { String path, String result ->
|
||||||
|
def outFile = new File(buildDir, path + '.html')
|
||||||
|
if (outFile.exists()) {
|
||||||
|
logger.info('outFile {} already exists, deleting', outFile)
|
||||||
|
outFile.delete()
|
||||||
|
}
|
||||||
|
outFile.createParentDirectories()
|
||||||
|
logger.info('writing result to {}', outFile)
|
||||||
|
outFile.write(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate pages from each text
|
||||||
texts.each {
|
texts.each {
|
||||||
logger.info('processing text: {}', it.path)
|
logger.info('processing text: {}', it.path)
|
||||||
|
|
||||||
@ -53,6 +71,9 @@ class SimpleStaticSiteGenerator implements StaticSiteGenerator {
|
|||||||
if (desiredTemplate == null) {
|
if (desiredTemplate == null) {
|
||||||
throw new IllegalArgumentException('in text ' + it.path + ' frontMatter.template must not be null')
|
throw new IllegalArgumentException('in text ' + it.path + ' frontMatter.template must not be null')
|
||||||
}
|
}
|
||||||
|
if (desiredTemplate.isEmpty() || desiredTemplate.isBlank()) {
|
||||||
|
throw new IllegalArgumentException('in text ' + it.path + ' frontMatter.template must not be empty, blank, or missing')
|
||||||
|
}
|
||||||
def template = templates.find {
|
def template = templates.find {
|
||||||
it.relativePath == desiredTemplate
|
it.relativePath == desiredTemplate
|
||||||
}
|
}
|
||||||
@ -66,15 +87,20 @@ class SimpleStaticSiteGenerator implements StaticSiteGenerator {
|
|||||||
logger.debug('result: {}', result)
|
logger.debug('result: {}', result)
|
||||||
|
|
||||||
// Output the result to the outfile, an .html file
|
// Output the result to the outfile, an .html file
|
||||||
def outFile = new File(buildDir, it.path + '.html')
|
outputPage(it.path, result)
|
||||||
if (outFile.exists()) {
|
|
||||||
logger.info('outFile {} already exists, deleting', outFile)
|
|
||||||
outFile.delete()
|
|
||||||
}
|
|
||||||
outFile.createParentDirectories()
|
|
||||||
logger.info('writing result to {}', outFile)
|
|
||||||
outFile.write(result)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Generate special pages
|
||||||
|
specialPages.each {
|
||||||
|
logger.info('processing specialPage: {}', it)
|
||||||
|
|
||||||
|
def result = it.type.renderer.render(it.text, texts, parts)
|
||||||
|
logger.info('result: {}', result)
|
||||||
|
|
||||||
|
// Output result to file
|
||||||
|
outputPage(it.path, result)
|
||||||
|
}
|
||||||
|
|
||||||
logger.trace(exit, '')
|
logger.trace(exit, '')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,14 @@
|
|||||||
|
package com.jessebrault.ssg.part
|
||||||
|
|
||||||
|
import groovy.transform.TupleConstructor
|
||||||
|
|
||||||
|
@TupleConstructor(includeFields = true, defaults = false)
|
||||||
|
class EmbeddablePart {
|
||||||
|
|
||||||
|
private final Part part
|
||||||
|
|
||||||
|
String render(Map binding) {
|
||||||
|
part.type.renderer.render(part.text, binding)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,6 +1,7 @@
|
|||||||
package com.jessebrault.ssg.part
|
package com.jessebrault.ssg.part
|
||||||
|
|
||||||
import com.jessebrault.ssg.util.FileNameHandler
|
import com.jessebrault.ssg.util.FileNameHandler
|
||||||
|
import groovy.io.FileType
|
||||||
import groovy.transform.NullCheck
|
import groovy.transform.NullCheck
|
||||||
import groovy.transform.TupleConstructor
|
import groovy.transform.TupleConstructor
|
||||||
import org.slf4j.Logger
|
import org.slf4j.Logger
|
||||||
@ -28,14 +29,14 @@ class PartFilePartsProvider implements PartsProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
def parts = []
|
def parts = []
|
||||||
partsDir.eachFileRecurse {
|
partsDir.eachFileRecurse(FileType.FILES) {
|
||||||
if (it.isFile()) {
|
def type = this.getPartType(it)
|
||||||
def type = this.getPartType(it)
|
if (type != null) {
|
||||||
if (type != null) {
|
def relativePath = this.partsDir.relativePath(it)
|
||||||
def relativePath = this.partsDir.relativePath(it)
|
logger.debug('found part {}', relativePath)
|
||||||
logger.debug('found part {}', relativePath)
|
parts << new Part(relativePath, type, it.text)
|
||||||
parts << new Part(relativePath, type, it.text)
|
} else {
|
||||||
}
|
logger.warn('ignoring {} since there is no partType for it', it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
parts
|
parts
|
||||||
|
@ -1,34 +1,21 @@
|
|||||||
package com.jessebrault.ssg.part
|
package com.jessebrault.ssg.part
|
||||||
|
|
||||||
import groovy.transform.TupleConstructor
|
|
||||||
|
|
||||||
class PartsMap {
|
class PartsMap {
|
||||||
|
|
||||||
@TupleConstructor(includeFields = true, defaults = false)
|
private final Map<String, EmbeddablePart> partsMap = [:]
|
||||||
class RenderingPart {
|
|
||||||
|
|
||||||
private final Part part
|
|
||||||
|
|
||||||
String render(Map binding) {
|
|
||||||
part.type.renderer.render(part.text, binding)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private final Map<String, RenderingPart> partsMap = new LinkedHashMap<>()
|
|
||||||
|
|
||||||
PartsMap(Collection<Part> parts) {
|
PartsMap(Collection<Part> parts) {
|
||||||
Objects.requireNonNull(parts)
|
Objects.requireNonNull(parts)
|
||||||
parts.each {
|
parts.each {
|
||||||
this.partsMap.put(it.name, new RenderingPart(it))
|
this.partsMap.put(it.name, new EmbeddablePart(it))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
RenderingPart get(String name) {
|
EmbeddablePart get(String name) {
|
||||||
this.partsMap.get(Objects.requireNonNull(name))
|
this.partsMap.get(Objects.requireNonNull(name))
|
||||||
}
|
}
|
||||||
|
|
||||||
RenderingPart getAt(String name) {
|
EmbeddablePart getAt(String name) {
|
||||||
this.get(Objects.requireNonNull(name))
|
this.get(Objects.requireNonNull(name))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,22 @@
|
|||||||
|
package com.jessebrault.ssg.specialpage
|
||||||
|
|
||||||
|
import com.jessebrault.ssg.part.Part
|
||||||
|
import com.jessebrault.ssg.part.PartsMap
|
||||||
|
import com.jessebrault.ssg.text.Text
|
||||||
|
import com.jessebrault.ssg.text.TextsMap
|
||||||
|
import groovy.text.GStringTemplateEngine
|
||||||
|
import groovy.text.TemplateEngine
|
||||||
|
|
||||||
|
class GspSpecialPageRenderer implements SpecialPageRenderer {
|
||||||
|
|
||||||
|
private static final TemplateEngine engine = new GStringTemplateEngine()
|
||||||
|
|
||||||
|
@Override
|
||||||
|
String render(String text, Collection<Text> texts, Collection<Part> parts) {
|
||||||
|
engine.createTemplate(text).make([
|
||||||
|
texts: new TextsMap(texts),
|
||||||
|
parts: new PartsMap(parts)
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,14 @@
|
|||||||
|
package com.jessebrault.ssg.specialpage
|
||||||
|
|
||||||
|
import groovy.transform.Canonical
|
||||||
|
import groovy.transform.NullCheck
|
||||||
|
import groovy.transform.TupleConstructor
|
||||||
|
|
||||||
|
@Canonical
|
||||||
|
@TupleConstructor(defaults = false)
|
||||||
|
@NullCheck
|
||||||
|
class SpecialPage {
|
||||||
|
String text
|
||||||
|
String path
|
||||||
|
SpecialPageType type
|
||||||
|
}
|
@ -0,0 +1,47 @@
|
|||||||
|
package com.jessebrault.ssg.specialpage
|
||||||
|
|
||||||
|
import com.jessebrault.ssg.util.FileNameHandler
|
||||||
|
import com.jessebrault.ssg.util.RelativePathHandler
|
||||||
|
import groovy.io.FileType
|
||||||
|
import groovy.transform.NullCheck
|
||||||
|
import groovy.transform.TupleConstructor
|
||||||
|
import org.slf4j.Logger
|
||||||
|
import org.slf4j.LoggerFactory
|
||||||
|
|
||||||
|
@TupleConstructor(includeFields = true, defaults = false)
|
||||||
|
@NullCheck
|
||||||
|
class SpecialPageFileSpecialPagesProvider implements SpecialPagesProvider {
|
||||||
|
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(SpecialPageFileSpecialPagesProvider)
|
||||||
|
|
||||||
|
private final Collection<SpecialPageType> specialPageTypes
|
||||||
|
private final File specialPagesDir
|
||||||
|
|
||||||
|
private SpecialPageType getSpecialPageType(File file) {
|
||||||
|
this.specialPageTypes.find {
|
||||||
|
it.ids.contains(new FileNameHandler(file).getExtension())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
Collection<SpecialPage> getSpecialPages() {
|
||||||
|
if (!this.specialPagesDir.isDirectory()) {
|
||||||
|
throw new IllegalArgumentException('specialPagesDir must be a directory')
|
||||||
|
}
|
||||||
|
|
||||||
|
def specialPages = []
|
||||||
|
this.specialPagesDir.eachFileRecurse(FileType.FILES) {
|
||||||
|
def type = this.getSpecialPageType(it)
|
||||||
|
if (type != null) {
|
||||||
|
def relativePath = this.specialPagesDir.relativePath(it)
|
||||||
|
def path = new RelativePathHandler(relativePath).getWithoutExtension()
|
||||||
|
logger.info('found specialPage {} with type {}', path, type)
|
||||||
|
specialPages << new SpecialPage(it.text, path, type)
|
||||||
|
} else {
|
||||||
|
logger.warn('ignoring {} since there is no specialPageType for it', it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
specialPages
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,8 @@
|
|||||||
|
package com.jessebrault.ssg.specialpage
|
||||||
|
|
||||||
|
import com.jessebrault.ssg.part.Part
|
||||||
|
import com.jessebrault.ssg.text.Text
|
||||||
|
|
||||||
|
interface SpecialPageRenderer {
|
||||||
|
String render(String text, Collection<Text> texts, Collection <Part> parts)
|
||||||
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
package com.jessebrault.ssg.specialpage
|
||||||
|
|
||||||
|
import groovy.transform.Canonical
|
||||||
|
import groovy.transform.NullCheck
|
||||||
|
import groovy.transform.TupleConstructor
|
||||||
|
|
||||||
|
@Canonical
|
||||||
|
@TupleConstructor(defaults = false)
|
||||||
|
@NullCheck
|
||||||
|
class SpecialPageType {
|
||||||
|
Collection<String> ids
|
||||||
|
SpecialPageRenderer renderer
|
||||||
|
}
|
@ -0,0 +1,5 @@
|
|||||||
|
package com.jessebrault.ssg.specialpage
|
||||||
|
|
||||||
|
interface SpecialPagesProvider {
|
||||||
|
Collection<SpecialPage> getSpecialPages()
|
||||||
|
}
|
@ -1,6 +1,7 @@
|
|||||||
package com.jessebrault.ssg.template
|
package com.jessebrault.ssg.template
|
||||||
|
|
||||||
import com.jessebrault.ssg.util.FileNameHandler
|
import com.jessebrault.ssg.util.FileNameHandler
|
||||||
|
import groovy.io.FileType
|
||||||
import groovy.transform.NullCheck
|
import groovy.transform.NullCheck
|
||||||
import groovy.transform.TupleConstructor
|
import groovy.transform.TupleConstructor
|
||||||
import org.slf4j.Logger
|
import org.slf4j.Logger
|
||||||
@ -27,14 +28,14 @@ class TemplateFileTemplatesProvider implements TemplatesProvider {
|
|||||||
throw new IllegalArgumentException('templatesDir must be a directory')
|
throw new IllegalArgumentException('templatesDir must be a directory')
|
||||||
}
|
}
|
||||||
def templates = []
|
def templates = []
|
||||||
this.templatesDir.eachFileRecurse {
|
this.templatesDir.eachFileRecurse(FileType.FILES) {
|
||||||
if (it.isFile()) {
|
def type = this.getType(it)
|
||||||
def type = this.getType(it)
|
if (type != null) {
|
||||||
if (type != null) {
|
def relativePath = this.templatesDir.relativePath(it)
|
||||||
def relativePath = this.templatesDir.relativePath(it)
|
logger.debug('found template {}', relativePath)
|
||||||
logger.debug('found template {}', relativePath)
|
templates << new Template(it.text, relativePath, type)
|
||||||
templates << new Template(it.text, relativePath, type)
|
} else {
|
||||||
}
|
logger.warn('ignoring {} because there is no templateType for it', it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
templates
|
templates
|
||||||
|
@ -0,0 +1,21 @@
|
|||||||
|
package com.jessebrault.ssg.text
|
||||||
|
|
||||||
|
import groovy.transform.Memoized
|
||||||
|
import groovy.transform.TupleConstructor
|
||||||
|
|
||||||
|
@TupleConstructor(includeFields = true, defaults = false)
|
||||||
|
class EmbeddableText {
|
||||||
|
|
||||||
|
private final Text text
|
||||||
|
|
||||||
|
@Memoized
|
||||||
|
String render() {
|
||||||
|
this.text.type.renderer.render(this.text.text)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Memoized
|
||||||
|
FrontMatter getFrontMatter() {
|
||||||
|
this.text.type.frontMatterGetter.get(this.text.text)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -3,16 +3,25 @@ package com.jessebrault.ssg.text
|
|||||||
import groovy.transform.NullCheck
|
import groovy.transform.NullCheck
|
||||||
import groovy.transform.ToString
|
import groovy.transform.ToString
|
||||||
import groovy.transform.TupleConstructor
|
import groovy.transform.TupleConstructor
|
||||||
|
import org.slf4j.Logger
|
||||||
|
import org.slf4j.LoggerFactory
|
||||||
|
|
||||||
@TupleConstructor(includeFields = true, defaults = false)
|
@TupleConstructor(includeFields = true, defaults = false)
|
||||||
@NullCheck
|
@NullCheck
|
||||||
@ToString
|
@ToString(includeFields = true)
|
||||||
class FrontMatter {
|
class FrontMatter {
|
||||||
|
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(FrontMatter)
|
||||||
|
|
||||||
private final Map<String, List<String>> data
|
private final Map<String, List<String>> data
|
||||||
|
|
||||||
String get(String key) {
|
String get(String key) {
|
||||||
data[key][0]
|
if (data[key] != null) {
|
||||||
|
data[key][0]
|
||||||
|
} else {
|
||||||
|
logger.warn('no entry for key in frontMatter, returning empty string: {}', key)
|
||||||
|
''
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String getAt(String key) {
|
String getAt(String key) {
|
||||||
@ -20,7 +29,12 @@ class FrontMatter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
List<String> getList(String key) {
|
List<String> getList(String key) {
|
||||||
data[key]
|
if (data[key] != null) {
|
||||||
|
data[key]
|
||||||
|
} else {
|
||||||
|
logger.warn('no entry for key in frontMatter, returning empty list: {}', key)
|
||||||
|
[]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ package com.jessebrault.ssg.text
|
|||||||
|
|
||||||
import com.jessebrault.ssg.util.FileNameHandler
|
import com.jessebrault.ssg.util.FileNameHandler
|
||||||
import com.jessebrault.ssg.util.RelativePathHandler
|
import com.jessebrault.ssg.util.RelativePathHandler
|
||||||
|
import groovy.io.FileType
|
||||||
import groovy.transform.NullCheck
|
import groovy.transform.NullCheck
|
||||||
import groovy.transform.TupleConstructor
|
import groovy.transform.TupleConstructor
|
||||||
import org.slf4j.Logger
|
import org.slf4j.Logger
|
||||||
@ -29,15 +30,15 @@ class TextFileTextsProvider implements TextsProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
def textFiles = []
|
def textFiles = []
|
||||||
this.textsDir.eachFileRecurse {
|
this.textsDir.eachFileRecurse(FileType.FILES) {
|
||||||
if (it.isFile()) {
|
def type = this.getTextType(it)
|
||||||
def type = this.getTextType(it)
|
if (type != null) {
|
||||||
if (type != null) {
|
def relativePath = this.textsDir.relativePath(it)
|
||||||
def relativePath = this.textsDir.relativePath(it)
|
def path = new RelativePathHandler(relativePath).getWithoutExtension()
|
||||||
def path = new RelativePathHandler(relativePath).getWithoutExtension()
|
logger.debug('found textFile {} with type {}', path, type)
|
||||||
logger.debug('found textFile {} with type {}', path, type)
|
textFiles << new Text(it.text, path, type)
|
||||||
textFiles << new Text(it.text, path, type)
|
} else {
|
||||||
}
|
logger.warn('ignoring {} because there is no textType for it', it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
textFiles
|
textFiles
|
||||||
|
22
lib/src/main/groovy/com/jessebrault/ssg/text/TextsMap.groovy
Normal file
22
lib/src/main/groovy/com/jessebrault/ssg/text/TextsMap.groovy
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
package com.jessebrault.ssg.text
|
||||||
|
|
||||||
|
class TextsMap {
|
||||||
|
|
||||||
|
private final Map<String, EmbeddableText> textsMap = [:]
|
||||||
|
|
||||||
|
TextsMap(Collection<Text> texts) {
|
||||||
|
Objects.requireNonNull(texts)
|
||||||
|
texts.each {
|
||||||
|
this.textsMap.put(it.path, new EmbeddableText(it))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
EmbeddableText get(String path) {
|
||||||
|
this.textsMap.get(Objects.requireNonNull(path))
|
||||||
|
}
|
||||||
|
|
||||||
|
EmbeddableText getAt(String path) {
|
||||||
|
this.get(Objects.requireNonNull(path))
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -10,7 +10,12 @@ class FileNameHandler {
|
|||||||
private final File file
|
private final File file
|
||||||
|
|
||||||
String getExtension() {
|
String getExtension() {
|
||||||
this.file.name.substring(this.file.name.lastIndexOf('.'))
|
def lastIndexOfDot = this.file.name.lastIndexOf('.')
|
||||||
|
if (lastIndexOfDot == -1) {
|
||||||
|
''
|
||||||
|
} else {
|
||||||
|
this.file.name.substring(lastIndexOfDot)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String getWithoutExtension() {
|
String getWithoutExtension() {
|
||||||
|
@ -3,6 +3,9 @@ package com.jessebrault.ssg
|
|||||||
import com.jessebrault.ssg.part.GspPartRenderer
|
import com.jessebrault.ssg.part.GspPartRenderer
|
||||||
import com.jessebrault.ssg.part.PartFilePartsProvider
|
import com.jessebrault.ssg.part.PartFilePartsProvider
|
||||||
import com.jessebrault.ssg.part.PartType
|
import com.jessebrault.ssg.part.PartType
|
||||||
|
import com.jessebrault.ssg.specialpage.GspSpecialPageRenderer
|
||||||
|
import com.jessebrault.ssg.specialpage.SpecialPageFileSpecialPagesProvider
|
||||||
|
import com.jessebrault.ssg.specialpage.SpecialPageType
|
||||||
import com.jessebrault.ssg.template.GspTemplateRenderer
|
import com.jessebrault.ssg.template.GspTemplateRenderer
|
||||||
import com.jessebrault.ssg.template.TemplateType
|
import com.jessebrault.ssg.template.TemplateType
|
||||||
import com.jessebrault.ssg.template.TemplateFileTemplatesProvider
|
import com.jessebrault.ssg.template.TemplateFileTemplatesProvider
|
||||||
@ -21,6 +24,7 @@ class StaticSiteGeneratorTests {
|
|||||||
private File partsDir
|
private File partsDir
|
||||||
private File templatesDir
|
private File templatesDir
|
||||||
private File textsDir
|
private File textsDir
|
||||||
|
private File specialPagesDir
|
||||||
|
|
||||||
private StaticSiteGenerator ssg
|
private StaticSiteGenerator ssg
|
||||||
|
|
||||||
@ -29,19 +33,23 @@ class StaticSiteGeneratorTests {
|
|||||||
this.textsDir = File.createTempDir()
|
this.textsDir = File.createTempDir()
|
||||||
this.templatesDir = File.createTempDir()
|
this.templatesDir = File.createTempDir()
|
||||||
this.partsDir = File.createTempDir()
|
this.partsDir = File.createTempDir()
|
||||||
|
this.specialPagesDir = File.createTempDir()
|
||||||
|
|
||||||
def config = new Config(
|
def config = new Config(
|
||||||
textTypes: [new TextType(['.md'], new MarkdownTextRenderer(), new MarkdownFrontMatterGetter())],
|
textTypes: [new TextType(['.md'], new MarkdownTextRenderer(), new MarkdownFrontMatterGetter())],
|
||||||
templateTypes: [new TemplateType(['.gsp'], new GspTemplateRenderer())],
|
templateTypes: [new TemplateType(['.gsp'], new GspTemplateRenderer())],
|
||||||
partTypes: [new PartType(['.gsp'], new GspPartRenderer())],
|
partTypes: [new PartType(['.gsp'], new GspPartRenderer())],
|
||||||
|
specialPageTypes: [new SpecialPageType(['.gsp'], new GspSpecialPageRenderer())],
|
||||||
|
|
||||||
textsDir: this.textsDir,
|
textsDir: this.textsDir,
|
||||||
templatesDir: this.templatesDir,
|
templatesDir: this.templatesDir,
|
||||||
partsDir: this.partsDir,
|
partsDir: this.partsDir,
|
||||||
|
specialPagesDir: this.specialPagesDir,
|
||||||
|
|
||||||
textsProviderGetter: { Config config -> new TextFileTextsProvider(config.textTypes, config.textsDir) },
|
textsProviderGetter: { Config config -> new TextFileTextsProvider(config.textTypes, config.textsDir) },
|
||||||
templatesProviderGetter: { Config config -> new TemplateFileTemplatesProvider(config.templateTypes, config.templatesDir) },
|
templatesProviderGetter: { Config config -> new TemplateFileTemplatesProvider(config.templateTypes, config.templatesDir) },
|
||||||
partsProviderGetter: { Config config -> new PartFilePartsProvider(config.partTypes, config.partsDir) }
|
partsProviderGetter: { Config config -> new PartFilePartsProvider(config.partTypes, config.partsDir) },
|
||||||
|
specialPagesProviderGetter: { Config config -> new SpecialPageFileSpecialPagesProvider(config.specialPageTypes, config.specialPagesDir) }
|
||||||
)
|
)
|
||||||
this.ssg = new SimpleStaticSiteGenerator(config)
|
this.ssg = new SimpleStaticSiteGenerator(config)
|
||||||
}
|
}
|
||||||
@ -77,4 +85,18 @@ class StaticSiteGeneratorTests {
|
|||||||
assertEquals('<p><strong>Hello, World!</strong></p>\n', outFile.text)
|
assertEquals('<p><strong>Hello, World!</strong></p>\n', outFile.text)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void outputsSpecialPage() {
|
||||||
|
new FileTreeBuilder(this.specialPagesDir).file('special.gsp', $/<%= texts['test'].render() %>/$)
|
||||||
|
new FileTreeBuilder(this.templatesDir).file('template.gsp', '<%= 1 + 1 %>')
|
||||||
|
new FileTreeBuilder(this.textsDir).file('test.md', '---\ntemplate: template.gsp\n---\nHello, World!')
|
||||||
|
|
||||||
|
def buildDir = File.createTempDir()
|
||||||
|
this.ssg.generate(buildDir)
|
||||||
|
|
||||||
|
def outFile = new File(buildDir, 'special.html')
|
||||||
|
assertTrue(outFile.exists())
|
||||||
|
assertEquals('<p>Hello, World!</p>\n', outFile.text)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,56 @@
|
|||||||
|
package com.jessebrault.ssg.specialpage
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.BeforeEach
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals
|
||||||
|
|
||||||
|
class SpecialPageFileSpecialPagesProviderTests {
|
||||||
|
|
||||||
|
private static final SpecialPageType gspType = new SpecialPageType(['.gsp'], null)
|
||||||
|
|
||||||
|
private File specialPagesDir
|
||||||
|
private SpecialPagesProvider specialPagesProvider
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void beforeEach() {
|
||||||
|
this.specialPagesDir = File.createTempDir()
|
||||||
|
this.specialPagesProvider = new SpecialPageFileSpecialPagesProvider([gspType], this.specialPagesDir)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void findsSpecialPage() {
|
||||||
|
new FileTreeBuilder(this.specialPagesDir)
|
||||||
|
.file('test.gsp', '<%= "Hello, World!" %>')
|
||||||
|
|
||||||
|
def r = this.specialPagesProvider.getSpecialPages()
|
||||||
|
assertEquals(1, r.size())
|
||||||
|
def f0 = r[0]
|
||||||
|
assertEquals('test', f0.path)
|
||||||
|
assertEquals('<%= "Hello, World!" %>', f0.text)
|
||||||
|
assertEquals(gspType, f0.type)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void findsNestedSpecialPage() {
|
||||||
|
new FileTreeBuilder(this.specialPagesDir).dir('nested') {
|
||||||
|
file('nested.gsp', '<%= "Hello, World!" %>')
|
||||||
|
}
|
||||||
|
|
||||||
|
def r = this.specialPagesProvider.getSpecialPages()
|
||||||
|
assertEquals(1, r.size())
|
||||||
|
def f0 = r[0]
|
||||||
|
assertEquals('nested/nested', f0.path)
|
||||||
|
assertEquals('<%= "Hello, World!" %>', f0.text)
|
||||||
|
assertEquals(gspType, f0.type)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void ignoresUnsupportedFile() {
|
||||||
|
new FileTreeBuilder(this.specialPagesDir).file('.ignored', 'Ignored!')
|
||||||
|
|
||||||
|
def r = this.specialPagesProvider.getSpecialPages()
|
||||||
|
assertEquals(0, r.size())
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -22,7 +22,7 @@ class TextFileTextsProviderTests {
|
|||||||
void findsFile() {
|
void findsFile() {
|
||||||
new FileTreeBuilder(this.textsDir).file('test.md', '**Hello, World!**')
|
new FileTreeBuilder(this.textsDir).file('test.md', '**Hello, World!**')
|
||||||
|
|
||||||
def r = textsProvider.getTextFiles()
|
def r = this.textsProvider.getTextFiles()
|
||||||
assertEquals(1, r.size())
|
assertEquals(1, r.size())
|
||||||
def f0 = r[0]
|
def f0 = r[0]
|
||||||
assertEquals('test', f0.path)
|
assertEquals('test', f0.path)
|
||||||
@ -36,7 +36,7 @@ class TextFileTextsProviderTests {
|
|||||||
file('nested.md', '**Hello!**')
|
file('nested.md', '**Hello!**')
|
||||||
}
|
}
|
||||||
|
|
||||||
def r = textsProvider.getTextFiles()
|
def r = this.textsProvider.getTextFiles()
|
||||||
assertEquals(1, r.size())
|
assertEquals(1, r.size())
|
||||||
def f0 = r[0]
|
def f0 = r[0]
|
||||||
assertEquals('nested/nested', f0.path)
|
assertEquals('nested/nested', f0.path)
|
||||||
@ -48,7 +48,7 @@ class TextFileTextsProviderTests {
|
|||||||
void ignoresUnsupportedFile() {
|
void ignoresUnsupportedFile() {
|
||||||
new FileTreeBuilder(this.textsDir).file('.ignored', 'Ignored!')
|
new FileTreeBuilder(this.textsDir).file('.ignored', 'Ignored!')
|
||||||
|
|
||||||
def r = textsProvider.getTextFiles()
|
def r = this.textsProvider.getTextFiles()
|
||||||
assertEquals(0, r.size())
|
assertEquals(0, r.size())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user