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

View File

@ -1,19 +1,20 @@
package com.jessebrault.ssg package com.jessebrault.ssg
import com.jessebrault.ssg.frontmatter.FrontMatterGetter import com.jessebrault.ssg.pagetemplate.PageTemplateType
import com.jessebrault.ssg.renderer.Renderer import com.jessebrault.ssg.pagetemplate.PageTemplatesFactory
import com.jessebrault.ssg.template.TemplatesFactory import com.jessebrault.ssg.text.TextFileType
import com.jessebrault.ssg.textfile.TextFilesFactory import com.jessebrault.ssg.text.TextFilesFactory
import com.jessebrault.ssg.textrenderer.TextRenderer
import groovy.transform.Canonical import groovy.transform.Canonical
import groovy.transform.MapConstructor import groovy.transform.MapConstructor
import java.util.function.Function
@Canonical @Canonical
@MapConstructor @MapConstructor
class Config { class Config {
TextFilesFactory textFilesFactory Collection<TextFileType> textFileTypes
TemplatesFactory templatesFactory Collection<PageTemplateType> pageTemplateTypes
FrontMatterGetter markdownFrontMatterGetter
TextRenderer markdownRenderer Function<Config, TextFilesFactory> textFileFactoryGetter
Renderer gspRenderer 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 package com.jessebrault.ssg
import com.jessebrault.ssg.frontmatter.FrontMatterGetter import com.jessebrault.ssg.pagetemplate.PageTemplatesFactory
import com.jessebrault.ssg.template.TemplatesFactory import com.jessebrault.ssg.text.TextFilesFactory
import com.jessebrault.ssg.renderer.Renderer import com.jessebrault.ssg.util.FileNameHandler
import com.jessebrault.ssg.textfile.TextFile import com.jessebrault.ssg.util.RelativePathHandler
import com.jessebrault.ssg.textfile.TextFilesFactory
import com.jessebrault.ssg.textrenderer.TextRenderer
import org.slf4j.Logger import org.slf4j.Logger
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
@ -13,46 +11,61 @@ class StaticSiteGeneratorImpl implements StaticSiteGenerator {
private static final Logger logger = LoggerFactory.getLogger(StaticSiteGeneratorImpl) private static final Logger logger = LoggerFactory.getLogger(StaticSiteGeneratorImpl)
private static String stripExtension(String relativePath) { private final Config config
relativePath.substring(0, relativePath.lastIndexOf('.'))
}
private final TextFilesFactory textFilesFactory private final TextFilesFactory textFilesFactory
private final TemplatesFactory templateFactory private final PageTemplatesFactory pageTemplatesFactory
private final FrontMatterGetter markdownFrontMatterGetter
private final TextRenderer markdownRenderer
private final Renderer gspRenderer
StaticSiteGeneratorImpl(Config config) { StaticSiteGeneratorImpl(Config config) {
this.textFilesFactory = Objects.requireNonNull(config.textFilesFactory) this.config = config
this.templateFactory = Objects.requireNonNull(config.templatesFactory) this.textFilesFactory = config.textFileFactoryGetter.apply(config)
this.markdownFrontMatterGetter = Objects.requireNonNull(config.markdownFrontMatterGetter) this.pageTemplatesFactory = config.pageTemplatesFactoryGetter.apply(config)
this.markdownRenderer = Objects.requireNonNull(config.markdownRenderer)
this.gspRenderer = Objects.requireNonNull(config.gspRenderer)
} }
@Override @Override
void generate(SiteSpec spec) { void generate(SiteSpec spec) {
def textFiles = this.textFilesFactory.getTextFiles(spec.textsDir) def textFiles = this.textFilesFactory.getTextFiles(spec.textsDir)
def templates = this.templateFactory.getTemplates(spec.templatesDir) def pageTemplates = this.pageTemplatesFactory.getTemplates(spec.templatesDir)
textFiles.each { textFiles.each {
if (it.type == TextFile.Type.MARKDOWN) { logger.info('processing textFile: {}', it.relativePath)
def frontMatter = this.markdownFrontMatterGetter.get(it.file.text) def fileNameHandler = new FileNameHandler(it.file)
def desiredTemplate = frontMatter['template'] def textFileType = this.config.textFileTypes.find {
if (desiredTemplate == null) { it.extensions.contains(fileNameHandler.getExtension())
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.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.GStringTemplateEngine
import groovy.text.TemplateEngine import groovy.text.TemplateEngine
class GspRenderer implements Renderer { class GspRenderer implements PageRenderer {
private static final TemplateEngine engine = new GStringTemplateEngine() private static final TemplateEngine engine = new GStringTemplateEngine()
@Override @Override
String render(Template template, String text) { String render(PageTemplate template, String text) {
engine.createTemplate(template.file.text).make([ engine.createTemplate(template.file.text).make([
text: text 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 import groovy.transform.TupleConstructor
@TupleConstructor(includeFields = true) @TupleConstructor(includeFields = true)
@ToString
class FrontMatter { class FrontMatter {
private final Map<String, List<String>> data 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 { public interface FrontMatterGetter {
FrontMatter get(String text); 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.YamlFrontMatterExtension
import org.commonmark.ext.front.matter.YamlFrontMatterVisitor 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.ext.front.matter.YamlFrontMatterExtension
import org.commonmark.parser.Parser 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 import groovy.transform.Canonical
@Canonical @Canonical
class TextFileType { class TextFileType {
Collection<String> extensions Collection<String> extensions
TextRenderer renderer 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 package com.jessebrault.ssg
import com.jessebrault.ssg.frontmatter.MarkdownFrontMatterGetter import com.jessebrault.ssg.pagetemplate.GspRenderer
import com.jessebrault.ssg.renderer.GspRenderer import com.jessebrault.ssg.pagetemplate.PageTemplateType
import com.jessebrault.ssg.template.TemplatesFactoryImpl import com.jessebrault.ssg.pagetemplate.PageTemplatesFactoryImpl
import com.jessebrault.ssg.textfile.TextFilesFactoryImpl import com.jessebrault.ssg.text.MarkdownFrontMatterGetter
import com.jessebrault.ssg.textrenderer.MarkdownRenderer 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.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.assertEquals
import static org.junit.jupiter.api.Assertions.assertTrue import static org.junit.jupiter.api.Assertions.assertTrue
class StaticSiteGeneratorTests { class StaticSiteGeneratorTests {
private static final Logger logger = LoggerFactory.getLogger(StaticSiteGeneratorTests)
private final StaticSiteGenerator ssg = new StaticSiteGeneratorImpl(new Config( private final StaticSiteGenerator ssg = new StaticSiteGeneratorImpl(new Config(
textFilesFactory: new TextFilesFactoryImpl(), textFileTypes: [new TextFileType(['.md'], new MarkdownRenderer(), new MarkdownFrontMatterGetter())],
templatesFactory: new TemplatesFactoryImpl(), pageTemplateTypes: [new PageTemplateType(['.gsp'], new GspRenderer())],
markdownFrontMatterGetter: new MarkdownFrontMatterGetter(), textFileFactoryGetter: { Config config -> new TextFilesFactoryImpl(config.textFileTypes) },
markdownRenderer: new MarkdownRenderer(), pageTemplatesFactoryGetter: { Config config -> new PageTemplatesFactoryImpl(config.pageTemplateTypes) }
gspRenderer: new GspRenderer()
)) ))
@Test @Test
@ -33,7 +30,6 @@ class StaticSiteGeneratorTests {
new File(textsDir, 'test.md').write('---\ntemplate: test.gsp\n---\n**Hello, World!**') new File(textsDir, 'test.md').write('---\ntemplate: test.gsp\n---\n**Hello, World!**')
new File(templatesDir, 'test.gsp').write('<%= text %>') new File(templatesDir, 'test.gsp').write('<%= text %>')
def spec = new SiteSpec( def spec = new SiteSpec(
buildDir: buildDir, buildDir: buildDir,
textsDir: textsDir, 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 org.junit.jupiter.api.Test
import static org.junit.jupiter.api.Assertions.assertEquals 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 @Test
void findsTemplate() { void findsTemplate() {

View File

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

View File

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