From 599fb919c741c5f2d8d1a9db0636d0890cc58728 Mon Sep 17 00:00:00 2001 From: JesseBrault0709 <62299747+JesseBrault0709@users.noreply.github.com> Date: Sun, 2 Jun 2024 13:42:54 +0200 Subject: [PATCH] Refactoring of DefaultStaticSiteGenerator and related bugs. --- .../ssg/DefaultStaticSiteGenerator.groovy | 277 ++++++++---------- .../jessebrault/ssg/page/AbstractPage.groovy | 33 +++ .../jessebrault/ssg/page/DefaultPage.groovy | 24 -- .../ssg/page/DefaultWvcPage.groovy | 76 +++++ .../com/jessebrault/ssg/page/Page.groovy | 8 +- .../jessebrault/ssg/view/WvcCompiler.groovy | 42 +++ cli/src/main/resources/log4j2.xml | 1 + 7 files changed, 287 insertions(+), 174 deletions(-) create mode 100644 api/src/main/groovy/com/jessebrault/ssg/page/AbstractPage.groovy delete mode 100644 api/src/main/groovy/com/jessebrault/ssg/page/DefaultPage.groovy create mode 100644 api/src/main/groovy/com/jessebrault/ssg/page/DefaultWvcPage.groovy create mode 100644 api/src/main/groovy/com/jessebrault/ssg/view/WvcCompiler.groovy diff --git a/api/src/main/groovy/com/jessebrault/ssg/DefaultStaticSiteGenerator.groovy b/api/src/main/groovy/com/jessebrault/ssg/DefaultStaticSiteGenerator.groovy index 2c2c2ea..ee772f8 100644 --- a/api/src/main/groovy/com/jessebrault/ssg/DefaultStaticSiteGenerator.groovy +++ b/api/src/main/groovy/com/jessebrault/ssg/DefaultStaticSiteGenerator.groovy @@ -5,7 +5,7 @@ import com.jessebrault.ssg.buildscript.BuildScriptToBuildSpecConverter import com.jessebrault.ssg.buildscript.BuildSpec import com.jessebrault.ssg.buildscript.delegates.BuildDelegate import com.jessebrault.ssg.di.* -import com.jessebrault.ssg.page.DefaultPage +import com.jessebrault.ssg.page.DefaultWvcPage import com.jessebrault.ssg.page.Page import com.jessebrault.ssg.page.PageFactory import com.jessebrault.ssg.page.PageSpec @@ -13,23 +13,19 @@ import com.jessebrault.ssg.text.Text import com.jessebrault.ssg.util.Diagnostic import com.jessebrault.ssg.view.PageView import com.jessebrault.ssg.view.SkipTemplate +import com.jessebrault.ssg.view.WvcCompiler import com.jessebrault.ssg.view.WvcPageView import groovy.transform.TupleConstructor +import groowt.util.di.ObjectFactory import groowt.util.di.RegistryObjectFactory -import groowt.util.fp.either.Either -import groowt.view.component.ComponentTemplate -import groowt.view.component.ViewComponent -import groowt.view.component.compiler.ComponentTemplateClassFactory -import groowt.view.component.compiler.ComponentTemplateCompilerConfiguration +import groowt.util.fp.option.Option import groowt.view.component.compiler.DefaultComponentTemplateCompilerConfiguration import groowt.view.component.compiler.SimpleComponentTemplateClassFactory -import groowt.view.component.compiler.source.ComponentTemplateSource import groowt.view.component.factory.ComponentFactories import groowt.view.component.web.DefaultWebViewComponentContext import groowt.view.component.web.WebViewComponent +import groowt.view.component.web.WebViewComponentContext import groowt.view.component.web.WebViewComponentScope -import groowt.view.component.web.compiler.DefaultWebViewComponentTemplateCompileUnit -import groowt.view.component.web.lib.Each import io.github.classgraph.ClassGraph import org.slf4j.Logger import org.slf4j.LoggerFactory @@ -74,29 +70,111 @@ class DefaultStaticSiteGenerator implements StaticSiteGenerator { texts } - protected Either compileTemplate( - Class componentClass, - String resourceName, - ComponentTemplateCompilerConfiguration compilerConfiguration, - ComponentTemplateClassFactory templateClassFactory + protected WvcCompiler getWvcCompiler() { + def wvcCompilerConfiguration = new DefaultComponentTemplateCompilerConfiguration() + wvcCompilerConfiguration.groovyClassLoader = this.groovyClassLoader + def templateClassFactory = new SimpleComponentTemplateClassFactory(this.groovyClassLoader) + new WvcCompiler(wvcCompilerConfiguration, templateClassFactory) + } + + protected WebViewComponentContext makeContext( + Set> allWvc, + RegistryObjectFactory objectFactory, + WvcPageView pageView ) { - def templateUrl = componentClass.getResource(resourceName) - if (templateUrl == null) { - return Either.left(new Diagnostic( - "Could not find templateResource: $resourceName" + new DefaultWebViewComponentContext().tap { + configureRootScope(WebViewComponentScope) { + // custom components + allWvc.each { wvcClass -> + //noinspection GroovyAssignabilityCheck + add(wvcClass, ComponentFactories.ofClosureClassType(wvcClass) { Map attr, Object[] args -> + WebViewComponent component + if (!attr.isEmpty() && args.length > 0) { + component = objectFactory.createInstance(wvcClass, attr, *args) + } else if (!attr.isEmpty()) { + component = objectFactory.createInstance(wvcClass, attr) + } else if (args.length > 0) { + component = objectFactory.createInstance(wvcClass, *args) + } else { + component = objectFactory.createInstance(wvcClass) + } + component.context = pageView.context + if (component.componentTemplate == null + && !wvcClass.isAnnotationPresent(SkipTemplate)) { + def compileResult = objectFactory.get(WvcCompiler).compileTemplate( + wvcClass, + wvcClass.simpleName + 'Template.wvc' + ) + if (compileResult.isRight()) { + component.componentTemplate = compileResult.getRight() + } else { + def left = compileResult.getLeft() + throw new RuntimeException(left.message, left.exception) + } + } + return component + }) + } + } + } + } + + protected Option handlePage( + String buildScriptFqn, + Page page, + BuildSpec buildSpec, + Set> allWvc, + ObjectFactory objectFactory + ) { + // instantiate PageView + def pageViewResult = page.createView() + if (pageViewResult.isLeft()) { + return Option.lift(pageViewResult.getLeft()) + } + PageView pageView = pageViewResult.getRight() + + // Prepare for rendering + pageView.pageTitle = page.name + pageView.url = buildSpec.baseUrl.get() + page.path + + if (pageView instanceof WvcPageView) { + pageView.context = this.makeContext(allWvc, objectFactory, pageView) + } + + // Render the page + def sw = new StringWriter() + try { + pageView.renderTo(sw) + } catch (Exception exception) { + return Option.lift(new Diagnostic( + "There was an exception while rendering $page.name as $pageView.class.name", + exception )) } - def source = ComponentTemplateSource.of(templateUrl) - def compileUnit = new DefaultWebViewComponentTemplateCompileUnit( - source.descriptiveName, - componentClass, - source, - componentClass.packageName - ) - def compileResult = compileUnit.compile(compilerConfiguration) - def templateClass = templateClassFactory.getTemplateClass(compileResult) - def componentTemplate = templateClass.getConstructor().newInstance() - return Either.right(componentTemplate) + + // Output the page if not dryRun + if (!this.dryRun) { + def outputDir = buildSpec.outputDir.get { + new SsgException("The outputDir Property in $buildScriptFqn must be set.") + } + outputDir.mkdirs() + + def outputFile = new File( + outputDir, + page.path.replace('/', File.separator) + page.fileExtension + ) + outputFile.parentFile.mkdirs() + try { + outputFile.write(sw.toString()) + } catch (Exception exception) { + return Option.lift(new Diagnostic( + "There was an exception while writing $page.name to $outputFile", + exception + )) + } + } + + return Option.empty() } @Override @@ -106,6 +184,8 @@ class DefaultStaticSiteGenerator implements StaticSiteGenerator { String buildScriptFqn, Map buildScriptCliArgs ) { + def wvcCompiler = this.getWvcCompiler() + // run build script(s) and get buildSpec def buildScriptGetter = new BuildScriptGetter( this.groovyClassLoader, @@ -126,7 +206,7 @@ class DefaultStaticSiteGenerator implements StaticSiteGenerator { ) } - // configure for Page instantiation + // configure objectFactory for Page/PageFactory instantiation objectFactoryBuilder.configureRegistry { // extensions addExtension(new TextsExtension().tap { @@ -151,6 +231,7 @@ class DefaultStaticSiteGenerator implements StaticSiteGenerator { bind(named('baseUrl', String), toSingleton(buildSpec.baseUrl.get { new SsgException("The baseUrl Property in $buildScriptFqn must be set.") })) + bind(WvcCompiler, toSingleton(wvcCompiler)) } // get the objectFactory @@ -178,30 +259,31 @@ class DefaultStaticSiteGenerator implements StaticSiteGenerator { def pageSpecInfo = pageViewInfo.getAnnotationInfo(PageSpec) if (pageSpecInfo != null) { def pageSpec = (PageSpec) pageSpecInfo.loadClassAndInstantiate() - pages << new DefaultPage( - pageSpec.name(), - pageSpec.path(), - pageSpec.fileExtension(), - (Class) pageViewInfo.loadClass(), - !pageSpec.templateResource().empty + pages << new DefaultWvcPage( + name: pageSpec.name(), + path: pageSpec.path(), + fileExtension: pageSpec.fileExtension(), + viewType: (Class) pageViewInfo.loadClass(), + templateResource: !pageSpec.templateResource().empty ? pageSpec.templateResource() - : pageViewInfo.simpleName + 'Template.wvc' + : pageViewInfo.simpleName + 'Template.wvc', + objectFactory: objectFactory, + wvcCompiler: wvcCompiler ) } } // page factories - def pageFactoryTypes = [] as Set> - def pageFactoryInfoList = scanResult.getClassesImplementing(PageFactory) - pageFactoryInfoList.each { pageFactoryInfo -> - pageFactoryTypes << (pageFactoryInfo.loadClass() as Class) + def pageFactoryTypes = pageFactoryInfoList.collect() { pageFactoryInfo -> + (pageFactoryInfo.loadClass() as Class) } // instantiate page factory and create the pages pageFactoryTypes.each { pageFactoryType -> def pageFactory = objectFactory.createInstance(pageFactoryType) - pages.addAll(pageFactory.create()) + def created = pageFactory.create() + pages.addAll(created) } // get all web view components @@ -211,8 +293,8 @@ class DefaultStaticSiteGenerator implements StaticSiteGenerator { } } - // Configure for PageView instantiation - objectFactoryBuilder.configureRegistry { + // Configure objectFactory for PageView instantiation with the Pages we found/created + objectFactory.configureRegistry { // extensions addExtension(new PagesExtension().tap { allPages.addAll(pages) @@ -221,112 +303,11 @@ class DefaultStaticSiteGenerator implements StaticSiteGenerator { } def diagnostics = [] as Collection - def wvcCompilerConfiguration = new DefaultComponentTemplateCompilerConfiguration() - wvcCompilerConfiguration.groovyClassLoader = this.groovyClassLoader - def componentTemplateClassFactory = new SimpleComponentTemplateClassFactory(this.groovyClassLoader) pages.each { - // instantiate PageView - PageView pageView - try { - objectFactory.registry.getExtension(SelfPageExtension).currentPage = it - pageView = objectFactory.createInstance(it.viewType) - } catch (Exception exception) { - diagnostics << new Diagnostic( - "There was an exception while constructing $it.viewType.name for $it.name", - exception - ) - return - } - - // Prepare for rendering - pageView.pageTitle = it.name - pageView.url = buildSpec.baseUrl.get() + it.path - if (pageView instanceof WvcPageView) { - pageView.context = new DefaultWebViewComponentContext().tap { - configureRootScope(WebViewComponentScope) { - // custom components - allWvc.each { wvcClass -> - //noinspection GroovyAssignabilityCheck - add(wvcClass, ComponentFactories.ofClosureClassType(wvcClass) { Map attr, Object[] args -> - WebViewComponent component - if (!attr.isEmpty() && args.length > 0) { - component = objectFactory.createInstance(wvcClass, attr, *args) - } else if (!attr.isEmpty()) { - component = objectFactory.createInstance(wvcClass, attr) - } else if (args.length > 0) { - component = objectFactory.createInstance(wvcClass, *args) - } else { - component = objectFactory.createInstance(wvcClass) - } - component.context = pageView.context - if (component.componentTemplate == null - && !wvcClass.isAnnotationPresent(SkipTemplate)) { - def compileResult = this.compileTemplate( - wvcClass, - wvcClass.simpleName + 'Template.wvc', - wvcCompilerConfiguration, - componentTemplateClassFactory - ) - if (compileResult.isRight()) { - component.componentTemplate = compileResult.getRight() - } else { - diagnostics << compileResult.getLeft() - } - } - return component - }) - } - } - } - - if (pageView.componentTemplate == null) { - def compileResult = this.compileTemplate( - pageView.class, - it.templateResource, - wvcCompilerConfiguration, - componentTemplateClassFactory - ) - if (compileResult.isRight()) { - pageView.componentTemplate = compileResult.getRight() - } else { - diagnostics << compileResult.getLeft() - return - } - } - } - - // Render the page - def sw = new StringWriter() - try { - pageView.renderTo(sw) - } catch (Exception exception) { - diagnostics << new Diagnostic( - "There was an exception while rendering $it.name as $pageView.class.name", - exception - ) - return - } - - // Output the page if not dryRun - if (!this.dryRun) { - def outputDir = buildSpec.outputDir.get { - new SsgException("The outputDir Property in $buildScriptFqn must be set.") - } - outputDir.mkdirs() - - def outputFile = new File( - outputDir, - it.path.replace('/', File.separator) + it.fileExtension - ) - try { - outputFile.write(sw.toString()) - } catch (Exception exception) { - diagnostics << new Diagnostic( - "There was an exception while writing $it.name to $outputFile", - exception - ) - } + def result = this.handlePage(buildScriptFqn, it, buildSpec, allWvc, objectFactory) + if (result.isPresent()) { + diagnostics << result.get() } } diff --git a/api/src/main/groovy/com/jessebrault/ssg/page/AbstractPage.groovy b/api/src/main/groovy/com/jessebrault/ssg/page/AbstractPage.groovy new file mode 100644 index 0000000..03b39c8 --- /dev/null +++ b/api/src/main/groovy/com/jessebrault/ssg/page/AbstractPage.groovy @@ -0,0 +1,33 @@ +package com.jessebrault.ssg.page + +abstract class AbstractPage implements Page { + + final String name + final String path + final String fileExtension + + AbstractPage(Map args) { + name = args.name + path = args.path + fileExtension = args.fileExtension + } + + @Override + int hashCode() { + Objects.hash(name, path, fileExtension) + } + + @Override + boolean equals(Object obj) { + if (this.is(obj)) { + return true + } else if (obj instanceof Page) { + return name == obj.name + && path == obj.path + && fileExtension == obj.fileExtension + } else { + return false + } + } + +} diff --git a/api/src/main/groovy/com/jessebrault/ssg/page/DefaultPage.groovy b/api/src/main/groovy/com/jessebrault/ssg/page/DefaultPage.groovy deleted file mode 100644 index a3e2e33..0000000 --- a/api/src/main/groovy/com/jessebrault/ssg/page/DefaultPage.groovy +++ /dev/null @@ -1,24 +0,0 @@ -package com.jessebrault.ssg.page - -import com.jessebrault.ssg.view.PageView -import groovy.transform.EqualsAndHashCode -import groovy.transform.NullCheck -import groovy.transform.TupleConstructor - -@TupleConstructor(defaults = false) -@NullCheck -@EqualsAndHashCode -class DefaultPage implements Page { - - final String name - final String path - final String fileExtension - final Class viewType - final String templateResource - - @Override - String toString() { - "SimplePage(name: $name, path: $path, fileExtension: $fileExtension, templateResource: $templateResource)" - } - -} diff --git a/api/src/main/groovy/com/jessebrault/ssg/page/DefaultWvcPage.groovy b/api/src/main/groovy/com/jessebrault/ssg/page/DefaultWvcPage.groovy new file mode 100644 index 0000000..263585f --- /dev/null +++ b/api/src/main/groovy/com/jessebrault/ssg/page/DefaultWvcPage.groovy @@ -0,0 +1,76 @@ +package com.jessebrault.ssg.page + +import com.jessebrault.ssg.di.SelfPageExtension +import com.jessebrault.ssg.util.Diagnostic +import com.jessebrault.ssg.view.PageView +import com.jessebrault.ssg.view.WvcCompiler +import com.jessebrault.ssg.view.WvcPageView +import groowt.util.di.RegistryObjectFactory +import groowt.util.fp.either.Either + +class DefaultWvcPage extends AbstractPage implements Page { + + final Class viewType + final String templateResource + final RegistryObjectFactory objectFactory + final WvcCompiler wvcCompiler + + DefaultWvcPage(Map args) { + super(args) + viewType = args.viewType + templateResource = args.templateResource ?: viewType.simpleName + 'Template.wvc' + objectFactory = args.objectFactory + wvcCompiler = args.wvcCompiler + } + + @Override + Either createView() { + WvcPageView pageView + try { + objectFactory.registry.getExtension(SelfPageExtension).currentPage = this + pageView = objectFactory.createInstance(viewType) + } catch (Exception exception) { + return Either.left(new Diagnostic( + "There was an exception while constructing $viewType.name for $name", + exception + )) + } + + if (pageView.componentTemplate == null) { + def compileResult = wvcCompiler.compileTemplate(viewType, templateResource) + if (compileResult.isRight()) { + pageView.componentTemplate = compileResult.getRight() + } else { + return Either.left(compileResult.getLeft()) + } + } + + return Either.right(pageView) + } + + @Override + int hashCode() { + Objects.hash(name, path, fileExtension, viewType, templateResource, objectFactory, wvcCompiler) + } + + @Override + boolean equals(Object obj) { + if (!super.equals(obj)) { + return false + } else if (obj instanceof DefaultWvcPage) { + return viewType == obj.viewType + && templateResource == obj.templateResource + && objectFactory == obj.objectFactory + && wvcCompiler == obj.wvcCompiler + } else { + return false + } + } + + @Override + String toString() { + "DefaultPage(name: $name, path: $path, fileExtension: $fileExtension, " + + "viewType: $viewType, templateResource: $templateResource)" + } + +} diff --git a/api/src/main/groovy/com/jessebrault/ssg/page/Page.groovy b/api/src/main/groovy/com/jessebrault/ssg/page/Page.groovy index c37a886..e0c77b0 100644 --- a/api/src/main/groovy/com/jessebrault/ssg/page/Page.groovy +++ b/api/src/main/groovy/com/jessebrault/ssg/page/Page.groovy @@ -1,11 +1,15 @@ package com.jessebrault.ssg.page +import com.jessebrault.ssg.util.Diagnostic import com.jessebrault.ssg.view.PageView +import groowt.util.fp.either.Either interface Page { + String getName() String getPath() String getFileExtension() - Class getViewType() - String getTemplateResource() + + Either createView() + } diff --git a/api/src/main/groovy/com/jessebrault/ssg/view/WvcCompiler.groovy b/api/src/main/groovy/com/jessebrault/ssg/view/WvcCompiler.groovy new file mode 100644 index 0000000..532e69c --- /dev/null +++ b/api/src/main/groovy/com/jessebrault/ssg/view/WvcCompiler.groovy @@ -0,0 +1,42 @@ +package com.jessebrault.ssg.view + +import com.jessebrault.ssg.util.Diagnostic +import groovy.transform.TupleConstructor +import groowt.util.fp.either.Either +import groowt.view.component.ComponentTemplate +import groowt.view.component.ViewComponent +import groowt.view.component.compiler.ComponentTemplateClassFactory +import groowt.view.component.compiler.ComponentTemplateCompilerConfiguration +import groowt.view.component.compiler.source.ComponentTemplateSource +import groowt.view.component.web.compiler.DefaultWebViewComponentTemplateCompileUnit + +@TupleConstructor +class WvcCompiler { + + final ComponentTemplateCompilerConfiguration compilerConfiguration + final ComponentTemplateClassFactory templateClassFactory + + Either compileTemplate( + Class componentClass, + String resourceName + ) { + def templateUrl = componentClass.getResource(resourceName) + if (templateUrl == null) { + return Either.left(new Diagnostic( + "Could not find templateResource: $resourceName" + )) + } + def source = ComponentTemplateSource.of(templateUrl) + def compileUnit = new DefaultWebViewComponentTemplateCompileUnit( + source.descriptiveName, + componentClass, + source, + componentClass.packageName + ) + def compileResult = compileUnit.compile(compilerConfiguration) + def templateClass = templateClassFactory.getTemplateClass(compileResult) + def componentTemplate = templateClass.getConstructor().newInstance() + return Either.right(componentTemplate) + } + +} diff --git a/cli/src/main/resources/log4j2.xml b/cli/src/main/resources/log4j2.xml index 99fd8eb..bf23852 100644 --- a/cli/src/main/resources/log4j2.xml +++ b/cli/src/main/resources/log4j2.xml @@ -14,5 +14,6 @@ +