Major refactoring.

This commit is contained in:
JesseBrault0709 2023-01-04 10:38:31 -06:00
parent 30861b3d44
commit 68c252d077
34 changed files with 272 additions and 248 deletions

View File

@ -1,30 +1,30 @@
package com.jessebrault.ssg
import com.jessebrault.ssg.frontmatter.MarkdownFrontMatterGetter
import com.jessebrault.ssg.renderer.GspRenderer
import com.jessebrault.ssg.template.TemplatesFactoryImpl
import com.jessebrault.ssg.textfile.TextFilesFactoryImpl
import com.jessebrault.ssg.textrenderer.MarkdownRenderer
import com.jessebrault.ssg.text.MarkdownFrontMatterGetter
import com.jessebrault.ssg.pagetemplate.GspRenderer
import com.jessebrault.ssg.pagetemplate.PageTemplatesFactoryImpl
import com.jessebrault.ssg.text.TextFilesFactoryImpl
import com.jessebrault.ssg.text.MarkdownRenderer
class StaticSiteGeneratorCli {
static void main(String[] args) {
def config = new Config(
textFilesFactory: new TextFilesFactoryImpl(),
templatesFactory: new TemplatesFactoryImpl(),
markdownFrontMatterGetter: new MarkdownFrontMatterGetter(),
markdownRenderer: new MarkdownRenderer(),
gspRenderer: new GspRenderer()
)
def ssg = new StaticSiteGeneratorImpl(config)
def spec = new SiteSpec(
buildDir: new File('build'),
staticDir: new File('static'),
textsDir: new File('texts'),
templatesDir: new File('templates'),
templatePartsDir: new File('templatePartsDir')
)
ssg.generate(spec)
// def config = new Config(
// textFilesFactory: new TextFilesFactoryImpl(),
// templatesFactory: new PageTemplatesFactoryImpl(),
// markdownFrontMatterGetter: new MarkdownFrontMatterGetter(),
// markdownRenderer: new MarkdownRenderer(),
// gspRenderer: new GspRenderer()
// )
// def ssg = new StaticSiteGeneratorImpl(config)
// def spec = new SiteSpec(
// buildDir: new File('build'),
// staticDir: new File('static'),
// textsDir: new File('texts'),
// templatesDir: new File('templates'),
// templatePartsDir: new File('templatePartsDir')
// )
// ssg.generate(spec)
}
}

View File

@ -1,19 +1,20 @@
package com.jessebrault.ssg
import com.jessebrault.ssg.frontmatter.FrontMatterGetter
import com.jessebrault.ssg.renderer.Renderer
import com.jessebrault.ssg.template.TemplatesFactory
import com.jessebrault.ssg.textfile.TextFilesFactory
import com.jessebrault.ssg.textrenderer.TextRenderer
import com.jessebrault.ssg.pagetemplate.PageTemplateType
import com.jessebrault.ssg.pagetemplate.PageTemplatesFactory
import com.jessebrault.ssg.text.TextFileType
import com.jessebrault.ssg.text.TextFilesFactory
import groovy.transform.Canonical
import groovy.transform.MapConstructor
import java.util.function.Function
@Canonical
@MapConstructor
class Config {
TextFilesFactory textFilesFactory
TemplatesFactory templatesFactory
FrontMatterGetter markdownFrontMatterGetter
TextRenderer markdownRenderer
Renderer gspRenderer
Collection<TextFileType> textFileTypes
Collection<PageTemplateType> pageTemplateTypes
Function<Config, TextFilesFactory> textFileFactoryGetter
Function<Config, PageTemplatesFactory> pageTemplatesFactoryGetter
}

View File

@ -0,0 +1,5 @@
package com.jessebrault.ssg
interface StaticSiteGenerator {
void generate(SiteSpec spec)
}

View File

@ -1,5 +0,0 @@
package com.jessebrault.ssg;
public interface StaticSiteGenerator {
void generate(SiteSpec spec);
}

View File

@ -1,11 +1,9 @@
package com.jessebrault.ssg
import com.jessebrault.ssg.frontmatter.FrontMatterGetter
import com.jessebrault.ssg.template.TemplatesFactory
import com.jessebrault.ssg.renderer.Renderer
import com.jessebrault.ssg.textfile.TextFile
import com.jessebrault.ssg.textfile.TextFilesFactory
import com.jessebrault.ssg.textrenderer.TextRenderer
import com.jessebrault.ssg.pagetemplate.PageTemplatesFactory
import com.jessebrault.ssg.text.TextFilesFactory
import com.jessebrault.ssg.util.FileNameHandler
import com.jessebrault.ssg.util.RelativePathHandler
import org.slf4j.Logger
import org.slf4j.LoggerFactory
@ -13,46 +11,61 @@ class StaticSiteGeneratorImpl implements StaticSiteGenerator {
private static final Logger logger = LoggerFactory.getLogger(StaticSiteGeneratorImpl)
private static String stripExtension(String relativePath) {
relativePath.substring(0, relativePath.lastIndexOf('.'))
}
private final Config config
private final TextFilesFactory textFilesFactory
private final TemplatesFactory templateFactory
private final FrontMatterGetter markdownFrontMatterGetter
private final TextRenderer markdownRenderer
private final Renderer gspRenderer
private final PageTemplatesFactory pageTemplatesFactory
StaticSiteGeneratorImpl(Config config) {
this.textFilesFactory = Objects.requireNonNull(config.textFilesFactory)
this.templateFactory = Objects.requireNonNull(config.templatesFactory)
this.markdownFrontMatterGetter = Objects.requireNonNull(config.markdownFrontMatterGetter)
this.markdownRenderer = Objects.requireNonNull(config.markdownRenderer)
this.gspRenderer = Objects.requireNonNull(config.gspRenderer)
this.config = config
this.textFilesFactory = config.textFileFactoryGetter.apply(config)
this.pageTemplatesFactory = config.pageTemplatesFactoryGetter.apply(config)
}
@Override
void generate(SiteSpec spec) {
def textFiles = this.textFilesFactory.getTextFiles(spec.textsDir)
def templates = this.templateFactory.getTemplates(spec.templatesDir)
def pageTemplates = this.pageTemplatesFactory.getTemplates(spec.templatesDir)
textFiles.each {
if (it.type == TextFile.Type.MARKDOWN) {
def frontMatter = this.markdownFrontMatterGetter.get(it.file.text)
def desiredTemplate = frontMatter['template']
if (desiredTemplate == null) {
throw new IllegalArgumentException('template must not be null')
}
def template = templates.find { it.relativePath == desiredTemplate }
def renderedText = this.markdownRenderer.render(it.file.text)
def result = this.gspRenderer.render(template, renderedText)
def outFile = new File(spec.buildDir, stripExtension(it.relativePath) + '.html')
if (outFile.exists()) {
outFile.delete()
}
outFile.createParentDirectories()
logger.debug('writing to outFile {}', outFile)
outFile.write(result)
logger.info('processing textFile: {}', it.relativePath)
def fileNameHandler = new FileNameHandler(it.file)
def textFileType = this.config.textFileTypes.find {
it.extensions.contains(fileNameHandler.getExtension())
}
logger.debug('textFileType: {}', textFileType)
if (textFileType == null) {
throw new IllegalArgumentException('unknown textFile type: ' + it.relativePath)
}
def renderedText = textFileType.renderer.render(it.file.text)
logger.debug('renderedText: {}', renderedText)
def frontMatter = textFileType.frontMatterGetter.get(it.file.text)
logger.debug('frontMatter: {}', frontMatter)
def desiredPageTemplate = frontMatter['template']
logger.debug('desiredPageTemplate name: {}', desiredPageTemplate)
if (desiredPageTemplate == null) {
throw new IllegalArgumentException('in textFile ' + it.relativePath + ' template must not be null')
}
def pageTemplate = pageTemplates.find {
it.relativePath == desiredPageTemplate
}
logger.debug('pageTemplate: {}', pageTemplate)
if (pageTemplate == null) {
throw new IllegalArgumentException('in textFile' + it.relativePath + ' unknown pageTemplate: ' + desiredPageTemplate)
}
def result = pageTemplate.type.renderer.render(pageTemplate, renderedText)
logger.debug('result: {}', result)
def outFile = new File(spec.buildDir, new RelativePathHandler(it.relativePath).getWithoutExtension() + '.html')
if (outFile.exists()) {
logger.info('outFile {} already exists, deleting', outFile)
outFile.delete()
}
outFile.createParentDirectories()
logger.info('writing result to {}', outFile)
outFile.write(result)
}
}

View File

@ -1,15 +1,15 @@
package com.jessebrault.ssg.renderer
package com.jessebrault.ssg.pagetemplate
import com.jessebrault.ssg.template.Template
import groovy.text.GStringTemplateEngine
import groovy.text.TemplateEngine
class GspRenderer implements Renderer {
class GspRenderer implements PageRenderer {
private static final TemplateEngine engine = new GStringTemplateEngine()
@Override
String render(Template template, String text) {
String render(PageTemplate template, String text) {
engine.createTemplate(template.file.text).make([
text: text
])

View File

@ -0,0 +1,5 @@
package com.jessebrault.ssg.pagetemplate
interface PageRenderer {
String render(PageTemplate template, String text)
}

View File

@ -0,0 +1,10 @@
package com.jessebrault.ssg.pagetemplate
import groovy.transform.Canonical
@Canonical
class PageTemplate {
File file
String relativePath
PageTemplateType type
}

View File

@ -0,0 +1,9 @@
package com.jessebrault.ssg.pagetemplate
import groovy.transform.Canonical
@Canonical
class PageTemplateType {
Collection<String> extensions
PageRenderer renderer
}

View File

@ -0,0 +1,5 @@
package com.jessebrault.ssg.pagetemplate
interface PageTemplatesFactory {
Collection<PageTemplate> getTemplates(File templatesDir)
}

View File

@ -0,0 +1,40 @@
package com.jessebrault.ssg.pagetemplate
import com.jessebrault.ssg.util.FileNameHandler
import groovy.transform.TupleConstructor
import org.slf4j.Logger
import org.slf4j.LoggerFactory
@TupleConstructor(includeFields = true)
class PageTemplatesFactoryImpl implements PageTemplatesFactory {
private static final Logger logger = LoggerFactory.getLogger(PageTemplatesFactoryImpl)
private final Collection<PageTemplateType> types
private PageTemplateType getType(File file) {
this.types.find {
it.extensions.contains(new FileNameHandler(file).getExtension())
}
}
@Override
Collection<PageTemplate> getTemplates(File templatesDir) {
if (!templatesDir.isDirectory()) {
throw new IllegalArgumentException('templatesDir must be a directory')
}
def templates = []
templatesDir.eachFileRecurse {
if (it.isFile()) {
def type = this.getType(it)
if (type != null) {
def relativePath = templatesDir.relativePath(it)
logger.debug('found template {}', relativePath)
templates << new PageTemplate(it, relativePath, type)
}
}
}
templates
}
}

View File

@ -1,7 +0,0 @@
package com.jessebrault.ssg.renderer;
import com.jessebrault.ssg.template.Template;
public interface Renderer {
String render(Template template, String text);
}

View File

@ -1,26 +0,0 @@
package com.jessebrault.ssg.template
import groovy.transform.Canonical
@Canonical
class Template {
enum Type {
GSP('.gsp');
private final String extension
Type(String extension) {
this.extension = extension
}
String getExtension() {
this.extension
}
}
File file
String relativePath
}

View File

@ -1,8 +0,0 @@
package com.jessebrault.ssg.template;
import java.io.File;
import java.util.Collection;
public interface TemplatesFactory {
Collection<Template> getTemplates(File templatesDir);
}

View File

@ -1,30 +0,0 @@
package com.jessebrault.ssg.template
import org.slf4j.Logger
import org.slf4j.LoggerFactory
class TemplatesFactoryImpl implements TemplatesFactory {
private static final Logger logger = LoggerFactory.getLogger(TemplatesFactoryImpl)
private static boolean hasTemplateExtension(File file) {
file.name.endsWith('.gsp')
}
@Override
Collection<Template> getTemplates(File templatesDir) {
if (!templatesDir.isDirectory()) {
throw new IllegalArgumentException('templatesDir must be a directory')
}
def templates = []
templatesDir.eachFileRecurse {
if (it.isFile() && hasTemplateExtension(it)) {
def relativePath = templatesDir.relativePath(it)
logger.debug('found template {}', relativePath)
templates << new Template(it, relativePath)
}
}
templates
}
}

View File

@ -1,8 +1,10 @@
package com.jessebrault.ssg.frontmatter
package com.jessebrault.ssg.text
import groovy.transform.ToString
import groovy.transform.TupleConstructor
@TupleConstructor(includeFields = true)
@ToString
class FrontMatter {
private final Map<String, List<String>> data

View File

@ -1,4 +1,4 @@
package com.jessebrault.ssg.frontmatter;
package com.jessebrault.ssg.text;
public interface FrontMatterGetter {
FrontMatter get(String text);

View File

@ -1,4 +1,4 @@
package com.jessebrault.ssg.frontmatter
package com.jessebrault.ssg.text
import org.commonmark.ext.front.matter.YamlFrontMatterExtension
import org.commonmark.ext.front.matter.YamlFrontMatterVisitor

View File

@ -1,4 +1,4 @@
package com.jessebrault.ssg.textrenderer
package com.jessebrault.ssg.text
import org.commonmark.ext.front.matter.YamlFrontMatterExtension
import org.commonmark.parser.Parser

View File

@ -0,0 +1,10 @@
package com.jessebrault.ssg.text
import groovy.transform.Canonical
@Canonical
class TextFile {
File file
String relativePath
TextFileType type
}

View File

@ -1,10 +1,10 @@
package com.jessebrault.ssg.textfile
package com.jessebrault.ssg.text
import com.jessebrault.ssg.textrenderer.TextRenderer
import groovy.transform.Canonical
@Canonical
class TextFileType {
Collection<String> extensions
TextRenderer renderer
FrontMatterGetter frontMatterGetter
}

View File

@ -0,0 +1,5 @@
package com.jessebrault.ssg.text
interface TextFilesFactory {
Collection<TextFile> getTextFiles(File textsDir)
}

View File

@ -0,0 +1,41 @@
package com.jessebrault.ssg.text
import com.jessebrault.ssg.util.FileNameHandler
import groovy.transform.TupleConstructor
import org.slf4j.Logger
import org.slf4j.LoggerFactory
@TupleConstructor(includeFields = true)
class TextFilesFactoryImpl implements TextFilesFactory {
private static final Logger logger = LoggerFactory.getLogger(TextFilesFactoryImpl)
private final Collection<TextFileType> textFileTypes
private TextFileType getTextFileType(File file) {
textFileTypes.find {
it.extensions.contains(new FileNameHandler(file).getExtension())
}
}
@Override
Collection<TextFile> getTextFiles(File textsDir) {
if (!textsDir.isDirectory()) {
throw new IllegalArgumentException('textsDir must be a directory')
}
def textFiles = []
textsDir.eachFileRecurse {
if (it.isFile()) {
def type = this.getTextFileType(it)
if (type != null) {
def relativePath = textsDir.relativePath(it)
logger.debug('found textFile {}', relativePath)
textFiles << new TextFile(it, relativePath, type)
}
}
}
textFiles
}
}

View File

@ -0,0 +1,5 @@
package com.jessebrault.ssg.text
interface TextRenderer {
String render(String text)
}

View File

@ -1,27 +0,0 @@
package com.jessebrault.ssg.textfile
import groovy.transform.Canonical
@Canonical
class TextFile {
enum Type {
MARKDOWN('.md');
private final String extension
Type(String extension) {
this.extension = extension
}
String getExtension() {
this.extension
}
}
File file
String relativePath
Type type
}

View File

@ -1,8 +0,0 @@
package com.jessebrault.ssg.textfile;
import java.io.File;
import java.util.Collection;
public interface TextFilesFactory {
Collection<TextFile> getTextFiles(File textsDir);
}

View File

@ -1,35 +0,0 @@
package com.jessebrault.ssg.textfile
import com.jessebrault.ssg.textfile.TextFile.Type
import org.slf4j.Logger
import org.slf4j.LoggerFactory
class TextFilesFactoryImpl implements TextFilesFactory {
private static final Logger logger = LoggerFactory.getLogger(TextFilesFactoryImpl)
private static Type getType(File file) {
Type.values().find {
file.name.endsWith(it.extension)
}
}
@Override
Collection<TextFile> getTextFiles(File textsDir) {
if (!textsDir.isDirectory()) {
throw new IllegalArgumentException('textsDir must be a directory')
}
def textFiles = []
textsDir.eachFileRecurse {
def type = getType(it)
if (type != null) {
def relativePath = textsDir.relativePath(it)
logger.debug('found textFile {}', relativePath)
textFiles << new TextFile(it, relativePath, type)
}
}
textFiles
}
}

View File

@ -1,5 +0,0 @@
package com.jessebrault.ssg.textrenderer;
public interface TextRenderer {
String render(String text);
}

View File

@ -0,0 +1,14 @@
package com.jessebrault.ssg.util
import groovy.transform.TupleConstructor
@TupleConstructor(includeFields = true)
class FileNameHandler {
private final File file
String getExtension() {
this.file.name.substring(this.file.name.lastIndexOf('.') + 1)
}
}

View File

@ -0,0 +1,14 @@
package com.jessebrault.ssg.util
import groovy.transform.TupleConstructor
@TupleConstructor(includeFields = true)
class RelativePathHandler {
private final String relativePath
String getWithoutExtension() {
this.relativePath.subSequence(0, this.relativePath.lastIndexOf('.'))
}
}

View File

@ -1,27 +1,24 @@
package com.jessebrault.ssg
import com.jessebrault.ssg.frontmatter.MarkdownFrontMatterGetter
import com.jessebrault.ssg.renderer.GspRenderer
import com.jessebrault.ssg.template.TemplatesFactoryImpl
import com.jessebrault.ssg.textfile.TextFilesFactoryImpl
import com.jessebrault.ssg.textrenderer.MarkdownRenderer
import com.jessebrault.ssg.pagetemplate.GspRenderer
import com.jessebrault.ssg.pagetemplate.PageTemplateType
import com.jessebrault.ssg.pagetemplate.PageTemplatesFactoryImpl
import com.jessebrault.ssg.text.MarkdownFrontMatterGetter
import com.jessebrault.ssg.text.MarkdownRenderer
import com.jessebrault.ssg.text.TextFileType
import com.jessebrault.ssg.text.TextFilesFactoryImpl
import org.junit.jupiter.api.Test
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import static org.junit.jupiter.api.Assertions.assertEquals
import static org.junit.jupiter.api.Assertions.assertTrue
class StaticSiteGeneratorTests {
private static final Logger logger = LoggerFactory.getLogger(StaticSiteGeneratorTests)
private final StaticSiteGenerator ssg = new StaticSiteGeneratorImpl(new Config(
textFilesFactory: new TextFilesFactoryImpl(),
templatesFactory: new TemplatesFactoryImpl(),
markdownFrontMatterGetter: new MarkdownFrontMatterGetter(),
markdownRenderer: new MarkdownRenderer(),
gspRenderer: new GspRenderer()
textFileTypes: [new TextFileType(['.md'], new MarkdownRenderer(), new MarkdownFrontMatterGetter())],
pageTemplateTypes: [new PageTemplateType(['.gsp'], new GspRenderer())],
textFileFactoryGetter: { Config config -> new TextFilesFactoryImpl(config.textFileTypes) },
pageTemplatesFactoryGetter: { Config config -> new PageTemplatesFactoryImpl(config.pageTemplateTypes) }
))
@Test
@ -33,7 +30,6 @@ class StaticSiteGeneratorTests {
new File(textsDir, 'test.md').write('---\ntemplate: test.gsp\n---\n**Hello, World!**')
new File(templatesDir, 'test.gsp').write('<%= text %>')
def spec = new SiteSpec(
buildDir: buildDir,
textsDir: textsDir,

View File

@ -1,12 +1,12 @@
package com.jessebrault.ssg.template
package com.jessebrault.ssg.pagetemplate
import org.junit.jupiter.api.Test
import static org.junit.jupiter.api.Assertions.assertEquals
class TemplatesFactoryTests {
class PageTemplatesFactoryTests {
private final TemplatesFactory templateFactory = new TemplatesFactoryImpl()
private final PageTemplatesFactory templateFactory = new PageTemplatesFactoryImpl()
@Test
void findsTemplate() {

View File

@ -1,4 +1,4 @@
package com.jessebrault.ssg.textfile
package com.jessebrault.ssg.text
import org.junit.jupiter.api.Test

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Configuration name="fauxtex" status="WARN">
<Configuration name="ssg" status="WARN">
<Appenders>
<Console name="standard" target="SYSTEM_OUT">
<PatternLayout>