Massive clean up and api/cli/gradle work. Running successfully now in test-ssg-project.
This commit is contained in:
		
							parent
							
								
									140dffefc6
								
							
						
					
					
						commit
						bc1d545297
					
				
							
								
								
									
										16
									
								
								.run/debug-test-project.run.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								.run/debug-test-project.run.xml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,16 @@ | ||||
| <component name="ProjectRunConfigurationManager"> | ||||
|   <configuration default="false" name="debug-test-project" type="Remote"> | ||||
|     <module name="ssg" /> | ||||
|     <option name="USE_SOCKET_TRANSPORT" value="true" /> | ||||
|     <option name="SERVER_MODE" value="false" /> | ||||
|     <option name="SHMEM_ADDRESS" /> | ||||
|     <option name="HOST" value="localhost" /> | ||||
|     <option name="PORT" value="8192" /> | ||||
|     <option name="AUTO_RESTART" value="false" /> | ||||
|     <RunnerSettings RunnerId="Debug"> | ||||
|       <option name="DEBUG_PORT" value="8192" /> | ||||
|       <option name="LOCAL" value="false" /> | ||||
|     </RunnerSettings> | ||||
|     <method v="2" /> | ||||
|   </configuration> | ||||
| </component> | ||||
							
								
								
									
										19
									
								
								.run/launch-debug-test-project.run.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								.run/launch-debug-test-project.run.xml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,19 @@ | ||||
| <component name="ProjectRunConfigurationManager"> | ||||
|   <configuration default="false" name="launch-debug-test-project" type="ShConfigurationType"> | ||||
|     <option name="SCRIPT_TEXT" value="" /> | ||||
|     <option name="INDEPENDENT_SCRIPT_PATH" value="true" /> | ||||
|     <option name="SCRIPT_PATH" value="$PROJECT_DIR$/test-ssg-project/bin/ssg" /> | ||||
|     <option name="SCRIPT_OPTIONS" value="--debug build -g --dry-run -b default" /> | ||||
|     <option name="INDEPENDENT_SCRIPT_WORKING_DIRECTORY" value="true" /> | ||||
|     <option name="SCRIPT_WORKING_DIRECTORY" value="$PROJECT_DIR$/test-ssg-project" /> | ||||
|     <option name="INDEPENDENT_INTERPRETER_PATH" value="true" /> | ||||
|     <option name="INTERPRETER_PATH" value="/usr/bin/env" /> | ||||
|     <option name="INTERPRETER_OPTIONS" value="" /> | ||||
|     <option name="EXECUTE_IN_TERMINAL" value="true" /> | ||||
|     <option name="EXECUTE_SCRIPT_FILE" value="true" /> | ||||
|     <envs /> | ||||
|     <method v="2"> | ||||
|       <option name="Gradle.BeforeRunTask" enabled="true" tasks="publishToMavenLocal" externalProjectPath="$PROJECT_DIR$" vmOptions="" scriptParameters="" /> | ||||
|     </method> | ||||
|   </configuration> | ||||
| </component> | ||||
							
								
								
									
										19
									
								
								.run/run-test-project.run.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								.run/run-test-project.run.xml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,19 @@ | ||||
| <component name="ProjectRunConfigurationManager"> | ||||
|   <configuration default="false" name="run-test-project" type="ShConfigurationType"> | ||||
|     <option name="SCRIPT_TEXT" value="" /> | ||||
|     <option name="INDEPENDENT_SCRIPT_PATH" value="true" /> | ||||
|     <option name="SCRIPT_PATH" value="$PROJECT_DIR$/test-ssg-project/bin/ssg" /> | ||||
|     <option name="SCRIPT_OPTIONS" value="build --log-level=debug --gradle --dry-run --build default" /> | ||||
|     <option name="INDEPENDENT_SCRIPT_WORKING_DIRECTORY" value="true" /> | ||||
|     <option name="SCRIPT_WORKING_DIRECTORY" value="$PROJECT_DIR$/test-ssg-project" /> | ||||
|     <option name="INDEPENDENT_INTERPRETER_PATH" value="true" /> | ||||
|     <option name="INTERPRETER_PATH" value="/usr/bin/env" /> | ||||
|     <option name="INTERPRETER_OPTIONS" value="" /> | ||||
|     <option name="EXECUTE_IN_TERMINAL" value="false" /> | ||||
|     <option name="EXECUTE_SCRIPT_FILE" value="true" /> | ||||
|     <envs /> | ||||
|     <method v="2"> | ||||
|       <option name="Gradle.BeforeRunTask" enabled="true" tasks="publishToMavenLocal" externalProjectPath="$PROJECT_DIR$" vmOptions="" scriptParameters="" /> | ||||
|     </method> | ||||
|   </configuration> | ||||
| </component> | ||||
| @ -3,6 +3,7 @@ plugins { | ||||
|     id 'groovy' | ||||
|     id 'java-library' | ||||
|     id 'java-test-fixtures' | ||||
|     id 'maven-publish' | ||||
| } | ||||
| 
 | ||||
| repositories { | ||||
| @ -18,26 +19,26 @@ configurations { | ||||
| 
 | ||||
| dependencies { | ||||
|     api libs.groovy | ||||
|     api libs.groovy.yaml | ||||
|     api libs.groowt.all | ||||
| 
 | ||||
|     compileOnlyApi libs.jetbrains.anontations | ||||
| 
 | ||||
|     implementation libs.groovy.templates | ||||
|     implementation libs.classgraph | ||||
|     implementation libs.commonmark | ||||
|     implementation libs.commonmark.frontmatter | ||||
|     implementation libs.jsoup | ||||
| 
 | ||||
|     // https://archiva.jessebrault.com/#artifact/com.jessebrault.gst/lib | ||||
|     implementation 'com.jessebrault.gst:lib:0.0.5' | ||||
| 
 | ||||
|     // https://mvnrepository.com/artifact/org.jgrapht/jgrapht-core | ||||
|     implementation 'org.jgrapht:jgrapht-core:1.5.2' | ||||
| 
 | ||||
|     // So we can use Grape dependency management | ||||
|     // https://mvnrepository.com/artifact/org.apache.ivy/ivy | ||||
|     runtimeOnly 'org.apache.ivy:ivy:2.5.2' | ||||
| } | ||||
| 
 | ||||
| jar { | ||||
|     archivesBaseName = 'ssg-api' | ||||
| } | ||||
| 
 | ||||
| publishing { | ||||
|     publications { | ||||
|         create('ssgApi', MavenPublication) { | ||||
|             artifactId = 'ssg-api' | ||||
|             from components.java | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -1,158 +0,0 @@ | ||||
| package com.jessebrault.ssg | ||||
| 
 | ||||
| import com.jessebrault.ssg.buildscript.BuildSpec | ||||
| import com.jessebrault.ssg.buildscript.BuildScriptConfiguratorFactory | ||||
| import com.jessebrault.ssg.buildscript.FileBuildScriptGetter | ||||
| import com.jessebrault.ssg.util.Diagnostic | ||||
| import groovy.transform.EqualsAndHashCode | ||||
| import groovy.transform.NullCheck | ||||
| import groovy.transform.TupleConstructor | ||||
| import org.jetbrains.annotations.Nullable | ||||
| import org.slf4j.Logger | ||||
| import org.slf4j.LoggerFactory | ||||
| import org.slf4j.Marker | ||||
| import org.slf4j.MarkerFactory | ||||
| 
 | ||||
| import java.util.function.Consumer | ||||
| 
 | ||||
| final class BuildScriptBasedStaticSiteGenerator implements StaticSiteGenerator { | ||||
| 
 | ||||
|     private static final Logger logger = LoggerFactory.getLogger(BuildScriptBasedStaticSiteGenerator) | ||||
|     private static final Marker enter = MarkerFactory.getMarker('enter') | ||||
|     private static final Marker exit = MarkerFactory.getMarker('exit') | ||||
| 
 | ||||
|     private final Collection<URL> buildScriptClassLoaderUrls | ||||
|     private final @Nullable File buildScript | ||||
|     private final Collection<BuildSpec> builds = [] | ||||
| 
 | ||||
|     private GroovyClassLoader buildScriptClassLoader | ||||
| 
 | ||||
|     /** | ||||
|      * @param buildScriptClassLoaderUrls the urls to pass to the buildScriptRunner's GroovyClassLoader. | ||||
|      *          These should include the URL needed to find the given buildScript file, if present, as | ||||
|      *          well as any script dependencies (such as the buildSrc dir). | ||||
|      * @param buildScript The buildScript File, may be <code>null</code>. | ||||
|      */ | ||||
|     BuildScriptBasedStaticSiteGenerator( | ||||
|             Collection<URL> buildScriptClassLoaderUrls, | ||||
|             @Nullable File buildScript = null | ||||
|     ) { | ||||
|         this.buildScriptClassLoaderUrls = buildScriptClassLoaderUrls | ||||
|         this.buildScript = buildScript | ||||
|     } | ||||
| 
 | ||||
|     private void runBuildScript( | ||||
|             Collection<BuildScriptConfiguratorFactory> configuratorFactories, | ||||
|             Map<String, Object> buildScriptArgs | ||||
|     ) { | ||||
|         logger.trace(enter, 'configuratorFactories: {}, buildScriptArgs: {}', configuratorFactories, buildScriptArgs) | ||||
| 
 | ||||
|         if (this.buildScript == null) { | ||||
|             logger.info('no specified build script; using defaults') | ||||
|             def result = FileBuildScriptGetter.runClosureScript { base -> | ||||
|                 configuratorFactories.each { | ||||
|                     it.get().accept(base) | ||||
|                 } | ||||
|             } | ||||
|             this.builds.addAll(result) | ||||
|         } else if (this.buildScript.exists() && this.buildScript.isFile()) { | ||||
|             logger.info('running buildScript: {}', this.buildScript) | ||||
|             def buildScriptRunner = new FileBuildScriptGetter(this.buildScriptClassLoaderUrls) | ||||
|             this.buildScriptClassLoader = buildScriptRunner.getBuildScriptClassLoader() | ||||
|             def result = buildScriptRunner.getBuildScript( | ||||
|                     this.buildScript.name, | ||||
|                     [args: buildScriptArgs] | ||||
|             ) { base -> | ||||
|                 configuratorFactories.each { it.get().accept(base) } | ||||
|             } | ||||
|             this.builds.addAll(result) | ||||
|         } else { | ||||
|             throw new IllegalArgumentException("given buildScript ${ this.buildScript } either does not exist or is not a file") | ||||
|         } | ||||
| 
 | ||||
|         logger.trace(exit, '') | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @return The classLoader used to load the buildScript. | ||||
|      * @throws NullPointerException if the buildScriptRunner was not initialized yet (make sure to call | ||||
|      *          {@link #doBuild} first). | ||||
|      */ | ||||
|     GroovyClassLoader getBuildScriptClassLoader() { | ||||
|         Objects.requireNonNull(this.buildScriptClassLoader) | ||||
|     } | ||||
| 
 | ||||
|     @TupleConstructor(defaults = false) | ||||
|     @NullCheck(includeGenerated = true) | ||||
|     @EqualsAndHashCode | ||||
|     private static final class IncludedBuildsResult { | ||||
|         final Collection<BuildSpec> builds | ||||
|         final Collection<Diagnostic> diagnostics | ||||
|     } | ||||
| 
 | ||||
|     // TODO: cache build script results | ||||
|     @Override | ||||
|     boolean doBuild( | ||||
|             String buildName, | ||||
|             Collection<BuildScriptConfiguratorFactory> configuratorFactories, | ||||
|             Map<String, Object> buildScriptArgs, | ||||
|             Consumer<Collection<Diagnostic>> diagnosticsConsumer | ||||
|     ) { | ||||
|         logger.trace(enter, 'buildName: {}, diagnosticsConsumer: {}', buildName, diagnosticsConsumer) | ||||
| 
 | ||||
|         this.runBuildScript(configuratorFactories, buildScriptArgs) | ||||
| 
 | ||||
|         def build = this.builds.find { it.name == buildName } | ||||
|         if (!build) { | ||||
|             throw new IllegalArgumentException("there is no registered build with name: ${ buildName }") | ||||
|         } | ||||
| 
 | ||||
|         def includedBuildsResult = build.includedBuilds.inject( | ||||
|                 new IncludedBuildsResult([], []) | ||||
|         ) { acc, includedBuildName -> | ||||
|             def includedBuild = this.builds.find { it.name == includedBuildName } | ||||
|             if (includedBuild == null) { | ||||
|                 acc.diagnostics << new Diagnostic("There is no registered build ${ includedBuildName } that can be included.") | ||||
|             } else { | ||||
|                 acc.builds << includedBuild | ||||
|             } | ||||
|             acc | ||||
|         } | ||||
| 
 | ||||
|         if (includedBuildsResult.diagnostics.size() > 0) { | ||||
|             diagnosticsConsumer.accept(includedBuildsResult.diagnostics) | ||||
|             logger.trace(exit, 'result: false') | ||||
|             return false | ||||
|         } | ||||
| 
 | ||||
|         def buildTasksConverter = new SimpleBuildTasksConverter() | ||||
| 
 | ||||
|         def allBuilds = includedBuildsResult.builds + build | ||||
|         def allBuildsConvertResults = allBuilds.collect { | ||||
|             buildTasksConverter.convert(it) | ||||
|         } | ||||
|         def allBuildsConvertDiagnostics = allBuildsConvertResults.collectMany { | ||||
|             it.diagnostics | ||||
|         } | ||||
| 
 | ||||
|         if (allBuildsConvertDiagnostics.size() > 0) { | ||||
|             diagnosticsConsumer.accept(allBuildsConvertDiagnostics) | ||||
|             logger.trace(exit, 'result: false') | ||||
|             return false | ||||
|         } | ||||
| 
 | ||||
|         def allTasks = allBuildsConvertResults.collectMany { | ||||
|             it.get() | ||||
|         } | ||||
|         def allTasksDiagnostics = allTasks.collectMany { it.execute(allTasks) } | ||||
|         if (allTasksDiagnostics.size() > 0) { | ||||
|             diagnosticsConsumer.accept(allTasksDiagnostics) | ||||
|             logger.trace(exit, 'result: false') | ||||
|             return false | ||||
|         } | ||||
| 
 | ||||
|         logger.trace(exit, 'result: true') | ||||
|         return true | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -1,9 +0,0 @@ | ||||
| package com.jessebrault.ssg | ||||
| 
 | ||||
| import com.jessebrault.ssg.buildscript.BuildSpec | ||||
| import com.jessebrault.ssg.task.Task | ||||
| import com.jessebrault.ssg.util.Result | ||||
| 
 | ||||
| interface BuildTasksConverter { | ||||
|     Result<Collection<Task>> convert(BuildSpec buildScriptResult) | ||||
| } | ||||
| @ -0,0 +1,228 @@ | ||||
| package com.jessebrault.ssg | ||||
| 
 | ||||
| import com.jessebrault.ssg.buildscript.BuildScriptGetter | ||||
| 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.Page | ||||
| import com.jessebrault.ssg.page.PageFactory | ||||
| import com.jessebrault.ssg.page.PageSpec | ||||
| import com.jessebrault.ssg.text.Text | ||||
| import com.jessebrault.ssg.util.Diagnostic | ||||
| import com.jessebrault.ssg.view.PageView | ||||
| import groovy.transform.TupleConstructor | ||||
| import groowt.util.di.RegistryObjectFactory | ||||
| import io.github.classgraph.ClassGraph | ||||
| import org.slf4j.Logger | ||||
| import org.slf4j.LoggerFactory | ||||
| 
 | ||||
| import java.nio.file.Files | ||||
| 
 | ||||
| import static groowt.util.di.BindingUtil.named | ||||
| import static groowt.util.di.BindingUtil.toSingleton | ||||
| 
 | ||||
| @TupleConstructor(includeFields = true, defaults = false) | ||||
| class DefaultStaticSiteGenerator implements StaticSiteGenerator { | ||||
| 
 | ||||
|     private static final Logger logger = LoggerFactory.getLogger(DefaultStaticSiteGenerator) | ||||
| 
 | ||||
|     protected final GroovyClassLoader groovyClassLoader | ||||
|     protected final URL[] buildScriptBaseUrls | ||||
|     protected final boolean dryRun | ||||
| 
 | ||||
|     protected Set<Text> getTexts(String buildScriptFqn, BuildSpec buildSpec) { | ||||
|         def textConverters = buildSpec.textConverters.get { | ||||
|             new SsgException("The textConverters Property in $buildScriptFqn must contain at least an empty Set.") | ||||
|         } | ||||
|         def textDirs = buildSpec.textsDirs.get { | ||||
|             new SsgException("The textDirs Property in $buildScriptFqn must contain at least an empty Set.") | ||||
|         } | ||||
|         def texts = [] as Set<Text> | ||||
|         textDirs.each { textDir -> | ||||
|             if (textDir.exists()) { | ||||
|                 Files.walk(textDir.toPath()).each { | ||||
|                     def asFile = it.toFile() | ||||
|                     def lastDot = asFile.name.lastIndexOf('.') | ||||
|                     if (lastDot != -1) { | ||||
|                         def extension = asFile.name.substring(lastDot) | ||||
|                         def converter = textConverters.find { | ||||
|                             it.handledExtensions.contains(extension) | ||||
|                         } | ||||
|                         texts << converter.convert(textDir, asFile) | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         texts | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     Collection<Diagnostic> doBuild( | ||||
|             File projectDir, | ||||
|             String buildName, | ||||
|             String buildScriptFqn, | ||||
|             Map<String, String> buildScriptCliArgs | ||||
|     ) { | ||||
|         // run build script(s) and get buildSpec | ||||
|         def buildScriptGetter = new BuildScriptGetter( | ||||
|                 this.groovyClassLoader, | ||||
|                 this.buildScriptBaseUrls, | ||||
|                 buildScriptCliArgs, | ||||
|                 projectDir | ||||
|         ) | ||||
|         def buildScriptToBuildSpecConverter = new BuildScriptToBuildSpecConverter( | ||||
|                 buildScriptGetter, | ||||
|                 BuildDelegate.withDefaults(projectDir) | ||||
|         ) | ||||
|         def buildSpec = buildScriptToBuildSpecConverter.convert(buildScriptFqn) | ||||
| 
 | ||||
|         // prepare objectFactory | ||||
|         def objectFactoryBuilder = buildSpec.objectFactoryBuilder.get { | ||||
|             new SsgException( | ||||
|                     "the objectFactoryBuilder Property in $buildScriptFqn " + | ||||
|                             "must contain a RegistryObjectFactory.Builder instance." | ||||
|             ) | ||||
|         } | ||||
| 
 | ||||
|         // configure for Page instantiation | ||||
|         objectFactoryBuilder.configureRegistry { | ||||
|             // extensions | ||||
|             addExtension(new TextsExtension().tap { | ||||
|                 allTexts.addAll(this.getTexts(buildScriptFqn, buildSpec)) | ||||
|             }) | ||||
|             addExtension(new ModelsExtension().tap { | ||||
|                 allModels.addAll(buildSpec.models.get { | ||||
|                     new SsgException("The models Property in $buildScriptFqn  must contain at least an empty Set.") | ||||
|                 }) | ||||
|             }) | ||||
|             addExtension(new GlobalsExtension().tap { | ||||
|                 globals.putAll(buildSpec.globals.get { | ||||
|                     new SsgException("The globals Property in $buildScriptFqn must contain at least an empty Map.") | ||||
|                 }) | ||||
|             }) | ||||
| 
 | ||||
|             // bindings | ||||
|             bind(named('buildName', String), toSingleton(buildSpec.name)) | ||||
|             bind(named('siteName', String), toSingleton(buildSpec.siteName.get { | ||||
|                 new SsgException("The siteName Property in $buildScriptFqn must be set.") | ||||
|             })) | ||||
|             bind(named('baseUrl', String), toSingleton(buildSpec.baseUrl.get { | ||||
|                 new SsgException("The baseUrl Property in $buildScriptFqn must be set.") | ||||
|             })) | ||||
|         } | ||||
| 
 | ||||
|         // get the objectFactory | ||||
|         def objectFactory = objectFactoryBuilder.build() | ||||
|         objectFactory.configureRegistry { | ||||
|             bind(RegistryObjectFactory, toSingleton(objectFactory)) | ||||
|         } | ||||
| 
 | ||||
|         // prepare for basePackages scan for Pages and PageFactories | ||||
|         def basePackages = buildSpec.basePackages.get { | ||||
|             new SsgException("The basePackages Property in $buildScriptFqn must contain at least an empty Set.") | ||||
|         } | ||||
|         def classgraph = new ClassGraph() | ||||
|                 .enableAnnotationInfo() | ||||
|                 .addClassLoader(groovyClassLoader) | ||||
|         basePackages.each { classgraph.acceptPackages(it) } | ||||
| 
 | ||||
| 
 | ||||
|         def pages = [] as Set<Page> | ||||
| 
 | ||||
|         try (def scanResult = classgraph.scan()) { | ||||
|             // single pages | ||||
|             def pageViewInfoList = scanResult.getClassesImplementing(PageView) | ||||
|             pageViewInfoList.each { pageViewInfo -> | ||||
|                 def pageSpecInfo = pageViewInfo.getAnnotationInfo(PageSpec) | ||||
|                 if (pageSpecInfo != null) { | ||||
|                     def pageSpec = (PageSpec) pageSpecInfo.loadClassAndInstantiate() | ||||
|                     pages << new DefaultPage( | ||||
|                             pageSpec.name(), | ||||
|                             pageSpec.path(), | ||||
|                             pageSpec.fileExtension(), | ||||
|                             (Class<? extends PageView>) pageViewInfo.loadClass() | ||||
|                     ) | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             // page factories | ||||
|             def pageFactoryTypes = [] as Set<Class<? extends PageFactory>> | ||||
| 
 | ||||
|             def pageFactoryInfoList = scanResult.getClassesImplementing(PageFactory) | ||||
|             pageFactoryInfoList.each { pageFactoryInfo -> | ||||
|                 pageFactoryTypes << (pageFactoryInfo.loadClass() as Class<? extends PageFactory>) | ||||
|             } | ||||
| 
 | ||||
|             // instantiate page factory and create the pages | ||||
|             pageFactoryTypes.each { pageFactoryType -> | ||||
|                 def pageFactory = objectFactory.createInstance(pageFactoryType) | ||||
|                 pages.addAll(pageFactory.create()) | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         // Configure for PageView instantiation | ||||
|         objectFactoryBuilder.configureRegistry { | ||||
|             // extensions | ||||
|             addExtension(new PagesExtension().tap { | ||||
|                 allPages.addAll(pages) | ||||
|             }) | ||||
|             addExtension(new SelfPageExtension()) | ||||
|         } | ||||
| 
 | ||||
|         def diagnostics = [] as Collection<Diagnostic> | ||||
| 
 | ||||
|         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 | ||||
|             } | ||||
| 
 | ||||
|             // 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 | ||||
|                     ) | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         // return diagnostics | ||||
|         diagnostics | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -1,33 +0,0 @@ | ||||
| package com.jessebrault.ssg | ||||
| 
 | ||||
| import com.jessebrault.ssg.buildscript.BuildSpec | ||||
| import com.jessebrault.ssg.task.Task | ||||
| import com.jessebrault.ssg.task.TaskSpec | ||||
| import com.jessebrault.ssg.util.Diagnostic | ||||
| import com.jessebrault.ssg.util.Result | ||||
| 
 | ||||
| final class SimpleBuildTasksConverter implements BuildTasksConverter { | ||||
| 
 | ||||
|     @Override | ||||
|     Result<Collection<Task>> convert(BuildSpec build) { | ||||
|         def taskSpec = new TaskSpec( | ||||
|                 build.name, | ||||
|                 build.outputDirFunction.apply(build).asFile(), | ||||
|                 build.siteSpec, | ||||
|                 build.globals | ||||
|         ) | ||||
|         Collection<Task> tasks = [] | ||||
|         Collection<Diagnostic> diagnostics = [] | ||||
| 
 | ||||
|         build.taskFactorySpecs.each { | ||||
|             def factory = it.supplier.get() | ||||
|             it.configurators.each { it.accept(factory) } | ||||
|             def result = factory.getTasks(taskSpec) | ||||
|             diagnostics.addAll(result.diagnostics) | ||||
|             tasks.addAll(result.get()) | ||||
|         } | ||||
| 
 | ||||
|         Result.of(diagnostics, tasks) | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -1,35 +0,0 @@ | ||||
| package com.jessebrault.ssg | ||||
| 
 | ||||
| import com.jessebrault.ssg.util.Monoid | ||||
| import com.jessebrault.ssg.util.Monoids | ||||
| import groovy.transform.EqualsAndHashCode | ||||
| import groovy.transform.NullCheck | ||||
| import groovy.transform.TupleConstructor | ||||
| 
 | ||||
| @TupleConstructor(defaults = false) | ||||
| @NullCheck(includeGenerated = true) | ||||
| @EqualsAndHashCode | ||||
| final class SiteSpec { | ||||
| 
 | ||||
|     static final Monoid<SiteSpec> DEFAULT_MONOID = Monoids.of(getBlank(), SiteSpec::concat) | ||||
| 
 | ||||
|     static SiteSpec getBlank() { | ||||
|         new SiteSpec('', '') | ||||
|     } | ||||
| 
 | ||||
|     static SiteSpec concat(SiteSpec s0, SiteSpec s1) { | ||||
|         new SiteSpec( | ||||
|                 s1.name.blank ? s0.name : s1.name, | ||||
|                 s1.baseUrl.blank ? s0.baseUrl : s1.baseUrl | ||||
|         ) | ||||
|     } | ||||
| 
 | ||||
|     final String name | ||||
|     final String baseUrl | ||||
| 
 | ||||
|     @Override | ||||
|     String toString() { | ||||
|         "SiteSpec(${ this.name }, ${ this.baseUrl })" | ||||
|     } | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										13
									
								
								api/src/main/groovy/com/jessebrault/ssg/SsgException.groovy
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								api/src/main/groovy/com/jessebrault/ssg/SsgException.groovy
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,13 @@ | ||||
| package com.jessebrault.ssg | ||||
| 
 | ||||
| class SsgException extends RuntimeException { | ||||
| 
 | ||||
|     SsgException(String message) { | ||||
|         super(message) | ||||
|     } | ||||
| 
 | ||||
|     SsgException(String message, Throwable cause) { | ||||
|         super(message, cause) | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -1,15 +1,12 @@ | ||||
| package com.jessebrault.ssg | ||||
| 
 | ||||
| import com.jessebrault.ssg.buildscript.BuildScriptConfiguratorFactory | ||||
| import com.jessebrault.ssg.util.Diagnostic | ||||
| 
 | ||||
| import java.util.function.Consumer | ||||
| 
 | ||||
| interface StaticSiteGenerator { | ||||
|     boolean doBuild( | ||||
|     Collection<Diagnostic> doBuild( | ||||
|             File projectDir, | ||||
|             String buildName, | ||||
|             Collection<BuildScriptConfiguratorFactory> configuratorFactories, | ||||
|             Map<String, Object> buildScriptArgs, | ||||
|             Consumer<Collection<Diagnostic>> diagnosticsConsumer | ||||
|             String buildScriptFqn, | ||||
|             Map<String, String> buildScriptCliArgs | ||||
|     ) | ||||
| } | ||||
| @ -1,38 +0,0 @@ | ||||
| package com.jessebrault.ssg.build | ||||
| 
 | ||||
| import com.jessebrault.ssg.model.Model | ||||
| import com.jessebrault.ssg.page.Page | ||||
| import groowt.util.di.RegistryObjectFactory | ||||
| import groowt.util.fp.provider.NamedSetProvider | ||||
| 
 | ||||
| import static com.jessebrault.ssg.util.ObjectUtil.* | ||||
| 
 | ||||
| class Build { | ||||
| 
 | ||||
|     final String name | ||||
|     final String siteName | ||||
|     final String baseUrl | ||||
|     final File outputDir | ||||
|     final Map globals | ||||
|     final Set<File> textsDirs | ||||
|     final NamedSetProvider<Page> pages | ||||
|     final RegistryObjectFactory objectFactory | ||||
| 
 | ||||
|     Build(Map args) { | ||||
|         this.name = requireString(args.name) | ||||
|         this.siteName = requireString(args.siteName) | ||||
|         this.baseUrl = requireString(args.baseUrl) | ||||
|         this.outputDir = requireFile(args.outputDir) | ||||
|         this.globals = requireMap(args.globals) | ||||
|         this.textsDirs = requireSet(args.textsDirs) | ||||
|         this.pages = requireType(NamedSetProvider, args.pages) | ||||
|         this.objectFactory = requireType(RegistryObjectFactory, args.objectFactory) | ||||
|     } | ||||
| 
 | ||||
|     void doBuild() { | ||||
|         // set up object factory for di | ||||
|         // container should have: Build and all its properties | ||||
|         // container should also have @Text, @Texts, @Page, and @Pages resolvers | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -13,26 +13,26 @@ import static java.util.Objects.requireNonNull | ||||
| @SuppressWarnings('unused') | ||||
| abstract class BuildScriptBase extends Script { | ||||
| 
 | ||||
|     /* --- Logging --- */ | ||||
| 
 | ||||
|     static final Logger logger = LoggerFactory.getLogger(BuildScriptBase) | ||||
|     static final Marker enter = MarkerFactory.getMarker('ENTER') | ||||
|     static final Marker exit = MarkerFactory.getMarker('EXIT') | ||||
| 
 | ||||
|     /* --- build script proper --- */ | ||||
| 
 | ||||
|     private String extending | ||||
|     private Closure buildClosure | ||||
|     private Closure buildClosure = { } | ||||
|     private File projectRoot | ||||
| 
 | ||||
|     File getProjectRoot() { | ||||
|         requireNonNull(this.projectRoot) | ||||
|     } | ||||
| 
 | ||||
|     void setProjectRoot(File projectRoot) { | ||||
|         this.projectRoot = requireNonNull(projectRoot) | ||||
|     } | ||||
|     /* --- Instance DSL helpers --- */ | ||||
| 
 | ||||
|     File file(String name) { | ||||
|         new File(this.projectRoot, name) | ||||
|     } | ||||
| 
 | ||||
|     /* --- DSL --- */ | ||||
| 
 | ||||
|     void build(@Nullable String extending, @DelegatesTo(value = BuildDelegate) Closure buildClosure) { | ||||
|         this.extending = extending | ||||
|         this.buildClosure = buildClosure | ||||
| @ -43,8 +43,21 @@ abstract class BuildScriptBase extends Script { | ||||
|         this.buildClosure = buildClosure | ||||
|     } | ||||
| 
 | ||||
|     /* --- internal --- */ | ||||
| 
 | ||||
|     @ApiStatus.Internal | ||||
|     @Nullable String getExtending() { | ||||
|     File getProjectRoot() { | ||||
|         requireNonNull(this.projectRoot) | ||||
|     } | ||||
| 
 | ||||
|     @ApiStatus.Internal | ||||
|     void setProjectRoot(File projectRoot) { | ||||
|         this.projectRoot = requireNonNull(projectRoot) | ||||
|     } | ||||
| 
 | ||||
|     @ApiStatus.Internal | ||||
|     @Nullable | ||||
|     String getExtending() { | ||||
|         this.extending | ||||
|     } | ||||
| 
 | ||||
|  | ||||
| @ -0,0 +1,29 @@ | ||||
| package com.jessebrault.ssg.buildscript | ||||
| 
 | ||||
| import groovy.transform.NullCheck | ||||
| import groovy.transform.TupleConstructor | ||||
| import org.codehaus.groovy.control.CompilerConfiguration | ||||
| 
 | ||||
| @NullCheck(includeGenerated = true) | ||||
| @TupleConstructor(includeFields = true, defaults = false) | ||||
| final class BuildScriptGetter { | ||||
| 
 | ||||
|     private final GroovyClassLoader groovyClassLoader | ||||
|     private final URL[] scriptBaseUrls | ||||
|     private final Map<String, String> scriptCliArgs | ||||
|     private final File projectDir | ||||
| 
 | ||||
|     BuildScriptBase getAndRunBuildScript(String fqn) { | ||||
|         def gcl = new GroovyClassLoader(this.groovyClassLoader, new CompilerConfiguration().tap { | ||||
|             it.scriptBaseClass = BuildScriptBase.name | ||||
|         }) | ||||
|         this.scriptBaseUrls.each { gcl.addURL(it) } | ||||
|         def scriptClass = gcl.loadClass(fqn, true, true) as Class<BuildScriptBase> | ||||
|         def buildScript = scriptClass.getConstructor().newInstance() | ||||
|         buildScript.binding = new Binding(this.scriptCliArgs) | ||||
|         buildScript.projectRoot = projectDir | ||||
|         buildScript.run() | ||||
|         buildScript | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -8,29 +8,32 @@ import java.util.function.Supplier | ||||
| 
 | ||||
| @NullCheck | ||||
| @TupleConstructor(includeFields = true) | ||||
| class BuildDelegateToBuildSpecConverter { | ||||
| class BuildScriptToBuildSpecConverter { | ||||
| 
 | ||||
|     private final FileBuildScriptGetter buildScriptGetter | ||||
|     private final BuildScriptGetter buildScriptGetter | ||||
|     private final Supplier<BuildDelegate> buildDelegateSupplier | ||||
| 
 | ||||
|     protected BuildSpec getFromDelegate(String name, BuildDelegate delegate) { | ||||
|         new BuildSpec( | ||||
|                 name: name, | ||||
|                 basePackages: delegate.basePackages, | ||||
|                 siteName: delegate.siteName, | ||||
|                 baseUrl: delegate.baseUrl, | ||||
|                 outputDir: delegate.outputDir, | ||||
|                 globals: delegate.globals, | ||||
|                 models: delegate.models, | ||||
|                 textsDirs: delegate.textsDirs, | ||||
|                 models: delegate.models | ||||
|                 textConverters: delegate.textConverters, | ||||
|                 objectFactoryBuilder: delegate.objectFactoryBuilder | ||||
|         ) | ||||
|     } | ||||
| 
 | ||||
|     BuildSpec convert(final String name, final BuildScriptBase buildScript) { | ||||
|     protected BuildSpec doConvert(String name, BuildScriptBase buildScript) { | ||||
|         final Deque<BuildScriptBase> buildHierarchy = new LinkedList<>() | ||||
|         buildHierarchy.push(buildScript) | ||||
|         String extending = buildScript.extending | ||||
|         while (extending != null) { | ||||
|             def from = this.buildScriptGetter.getBuildScript(extending) | ||||
|             def from = this.buildScriptGetter.getAndRunBuildScript(extending) | ||||
|             buildHierarchy.push(from) | ||||
|             extending = from.extending | ||||
|         } | ||||
| @ -45,4 +48,9 @@ class BuildDelegateToBuildSpecConverter { | ||||
|         this.getFromDelegate(name, delegate) | ||||
|     } | ||||
| 
 | ||||
|     BuildSpec convert(String buildScriptFqn) { | ||||
|         def start = this.buildScriptGetter.getAndRunBuildScript(buildScriptFqn) | ||||
|         this.doConvert(buildScriptFqn, start) | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -1,37 +1,48 @@ | ||||
| package com.jessebrault.ssg.buildscript | ||||
| 
 | ||||
| import com.jessebrault.ssg.model.Model | ||||
| import com.jessebrault.ssg.text.TextConverter | ||||
| import groovy.transform.EqualsAndHashCode | ||||
| import groovy.transform.NullCheck | ||||
| import groowt.util.fp.provider.NamedProvider | ||||
| import groowt.util.di.RegistryObjectFactory | ||||
| import groowt.util.fp.provider.Provider | ||||
| 
 | ||||
| import static com.jessebrault.ssg.util.ObjectUtil.* | ||||
| import static com.jessebrault.ssg.util.ObjectUtil.requireProvider | ||||
| import static com.jessebrault.ssg.util.ObjectUtil.requireString | ||||
| 
 | ||||
| @NullCheck(includeGenerated = true) | ||||
| @EqualsAndHashCode | ||||
| final class BuildSpec { | ||||
| 
 | ||||
|     final String name | ||||
|     final Provider<Set<String>> basePackages | ||||
|     final Provider<String> siteName | ||||
|     final Provider<String> baseUrl | ||||
|     final Provider<File> outputDir | ||||
|     final Provider<Map<String, Object>> globals | ||||
|     final Set<Provider<File>> textsDirs | ||||
|     final Provider<Set<Model>> models | ||||
|     final Provider<Set<File>> textsDirs | ||||
|     final Provider<Set<TextConverter>> textConverters | ||||
|     final Provider<RegistryObjectFactory.Builder> objectFactoryBuilder | ||||
| 
 | ||||
|     @SuppressWarnings('GroovyAssignabilityCheck') | ||||
|     BuildSpec(Map args) { | ||||
|         this.name = requireString(args.name) | ||||
|         this.basePackages = requireProvider(args.basePackages) | ||||
|         this.siteName = requireProvider(args.siteName) | ||||
|         this.baseUrl = requireProvider(args.baseUrl) | ||||
|         this.outputDir = requireProvider(args.outputDir) | ||||
|         this.globals = requireMap(args.globals) | ||||
|         this.textsDirs = requireSet(args.textsDirs) | ||||
|         this.globals = requireProvider(args.globals) | ||||
|         this.models = requireProvider(args.models) | ||||
|         this.textsDirs = requireProvider(args.textsDirs) | ||||
|         this.textConverters = requireProvider(args.textConverters) | ||||
|         this.objectFactoryBuilder = requireProvider(args.objectFactoryBuilder) | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     String toString() { | ||||
|         "Build(name: ${ this.name })" | ||||
|         "Build(name: ${this.name}, basePackages: $basePackages, siteName: $siteName, " + | ||||
|                 "baseUrl: $baseUrl, outputDir: $outputDir, textsDirs: $textsDirs)" | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  | ||||
| @ -1,19 +0,0 @@ | ||||
| package com.jessebrault.ssg.buildscript | ||||
| 
 | ||||
| import groovy.transform.NullCheck | ||||
| import groovy.transform.TupleConstructor | ||||
| 
 | ||||
| @NullCheck | ||||
| @TupleConstructor(includeFields = true) | ||||
| final class FileBuildScriptGetter { | ||||
| 
 | ||||
|     private final GroovyClassLoader groovyClassLoader | ||||
| 
 | ||||
|     BuildScriptBase getBuildScript(String name) { | ||||
|         Class<?> scriptClass = this.groovyClassLoader.loadClass(name, true, false) | ||||
|         def scriptObject = scriptClass.getConstructor().newInstance() | ||||
|         assert scriptObject instanceof BuildScriptBase | ||||
|         scriptObject | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -1,37 +1,61 @@ | ||||
| package com.jessebrault.ssg.buildscript.delegates | ||||
| 
 | ||||
| import groovy.transform.EqualsAndHashCode | ||||
| import groovy.transform.NullCheck | ||||
| import com.jessebrault.ssg.model.Model | ||||
| import com.jessebrault.ssg.model.Models | ||||
| import com.jessebrault.ssg.text.MarkdownTextConverter | ||||
| import com.jessebrault.ssg.text.TextConverter | ||||
| import groowt.util.di.DefaultRegistryObjectFactory | ||||
| import groowt.util.di.RegistryObjectFactory | ||||
| import groowt.util.fp.property.DefaultProperty | ||||
| import groowt.util.fp.property.Property | ||||
| import groowt.util.fp.provider.DefaultSetProvider | ||||
| import groowt.util.fp.provider.DefaultProvider | ||||
| import groowt.util.fp.provider.NamedProvider | ||||
| import groowt.util.fp.provider.Provider | ||||
| import groowt.util.fp.provider.SetProvider | ||||
| 
 | ||||
| import java.util.function.Supplier | ||||
| 
 | ||||
| @NullCheck(includeGenerated = true) | ||||
| @EqualsAndHashCode(includeFields = true) | ||||
| final class BuildDelegate { | ||||
| 
 | ||||
|     static Supplier<BuildDelegate> withDefaults() { | ||||
|     static Supplier<BuildDelegate> withDefaults(File projectDir) { | ||||
|         return { | ||||
|             new BuildDelegate().tap { | ||||
|                 outputDir.convention = 'dist' | ||||
|             new BuildDelegate(projectDir).tap { | ||||
|                 basePackages.convention = [] as Set<String> | ||||
|                 outputDir.convention = new File(projectDir, 'dist') | ||||
|                 globals.convention = [:] | ||||
|                 objectFactory.convention = DefaultRegistryObjectFactory.Builder.withDefaults() | ||||
|                 models.convention = [] as Set<Model> | ||||
|                 textsDirs.convention = [new File(projectDir, 'texts')] as Set<File> | ||||
|                 textConverters.convention = [new MarkdownTextConverter()] as Set<TextConverter> | ||||
|                 objectFactoryBuilder.convention = DefaultRegistryObjectFactory.Builder.withDefaults() | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     final Property<String> siteName = Property.empty() | ||||
|     final Property<String> baseUrl = Property.empty() | ||||
|     final Property<File> outputDir = Property.empty() | ||||
|     final Property<Map<String, Object>> globals = Property.empty() | ||||
|     final Property<RegistryObjectFactory> objectFactory = Property.empty() | ||||
|     final File projectDir | ||||
| 
 | ||||
|     private final Set<Provider<File>> textsDirs = [] | ||||
|     final Property<Set<String>> basePackages = DefaultProperty.<Set<String>>empty(Set) | ||||
|     final Property<String> siteName = DefaultProperty.empty(String) | ||||
|     final Property<String> baseUrl = DefaultProperty.empty(String) | ||||
|     final Property<File> outputDir = DefaultProperty.empty(File) | ||||
|     final Property<Map<String, Object>> globals = DefaultProperty.<Map<String, Object>>empty(Map) | ||||
|     final Property<Set<Model>> models = DefaultProperty.<Set<Model>>empty(Set) | ||||
|     final Property<Set<File>> textsDirs = DefaultProperty.<Set<File>>empty(Set) | ||||
|     final Property<Set<TextConverter>> textConverters = DefaultProperty.<Set<TextConverter>>empty(Set) | ||||
|     final Property<RegistryObjectFactory.Builder> objectFactoryBuilder = | ||||
|             DefaultProperty.empty(RegistryObjectFactory.Builder) | ||||
| 
 | ||||
|     private BuildDelegate(File projectDir) { | ||||
|         this.projectDir = projectDir | ||||
|     } | ||||
| 
 | ||||
|     /* TODO: add friendly DSL methods for setting all properties */ | ||||
| 
 | ||||
|     void basePackage(String toAdd) { | ||||
|         this.basePackages.configure { it.add(toAdd) } | ||||
|     } | ||||
| 
 | ||||
|     void basePackages(String... toAdd) { | ||||
|         toAdd.each { this.basePackage(it) } | ||||
|     } | ||||
| 
 | ||||
|     void siteName(String siteName) { | ||||
|         this.siteName.set(siteName) | ||||
| @ -62,25 +86,49 @@ final class BuildDelegate { | ||||
|         globalsClosure.delegate = globalsDelegate | ||||
|         globalsClosure.resolveStrategy = Closure.DELEGATE_FIRST | ||||
|         globalsClosure() | ||||
|         this.globals.set { | ||||
|         this.globals.set DefaultProvider.ofLazy(Map) { | ||||
|             this.globals.get() + globalsDelegate | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     void model(String name, Object obj) { | ||||
|         this.models.configure {it.add(Models.of(name, obj)) } | ||||
|     } | ||||
| 
 | ||||
|     void model(Model model) { | ||||
|         this.models.configure { it.add(model) } | ||||
|     } | ||||
| 
 | ||||
|     void model(String name, Provider tProvider) { | ||||
|         this.models.configure { it.add(Models.ofProvider(name, tProvider)) } | ||||
|     } | ||||
| 
 | ||||
|     <T> void model(String name, Class<T> type, Supplier<? extends T> tSupplier) { | ||||
|         this.models.configure { it.add(Models.ofSupplier(name, type, tSupplier)) } | ||||
|     } | ||||
| 
 | ||||
|     void model(NamedProvider namedProvider) { | ||||
|         this.models.configure { it.add(Models.ofNamedProvider(namedProvider)) } | ||||
|     } | ||||
| 
 | ||||
|     void textsDir(File textsDir) { | ||||
|         this.textsDirs.add { textsDir } | ||||
|         this.textsDirs.configure { it.add(textsDir) } | ||||
|     } | ||||
| 
 | ||||
|     void textsDir(Provider<File> textsDirProvider) { | ||||
|         this.textsDirs << textsDirProvider | ||||
|     void textsDirs(File... textsDirs) { | ||||
|         textsDirs.each { this.textsDir(it) } | ||||
|     } | ||||
| 
 | ||||
|     void textsDirs(Set<Provider<File>> textsDirProviders) { | ||||
|         textsDirProviders.each { this.textsDir(it) } | ||||
|     void textConverter(TextConverter textConverter) { | ||||
|         this.textConverters.configure { it.add(textConverter) } | ||||
|     } | ||||
| 
 | ||||
|     SetProvider<File> getTextsDirs() { | ||||
|         new DefaultSetProvider(this.textsDirs) | ||||
|     void textConverters(TextConverter... textConverters) { | ||||
|         textConverters.each { this.textConverter(it) } | ||||
|     } | ||||
| 
 | ||||
|     void objectFactoryBuilder(RegistryObjectFactory.Builder builder) { | ||||
|         this.objectFactoryBuilder.set(builder) | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  | ||||
							
								
								
									
										15
									
								
								api/src/main/groovy/com/jessebrault/ssg/di/Global.groovy
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								api/src/main/groovy/com/jessebrault/ssg/di/Global.groovy
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,15 @@ | ||||
| package com.jessebrault.ssg.di | ||||
| 
 | ||||
| import jakarta.inject.Qualifier | ||||
| 
 | ||||
| import java.lang.annotation.ElementType | ||||
| import java.lang.annotation.Retention | ||||
| import java.lang.annotation.RetentionPolicy | ||||
| import java.lang.annotation.Target | ||||
| 
 | ||||
| @Qualifier | ||||
| @Retention(RetentionPolicy.RUNTIME) | ||||
| @Target([ElementType.PARAMETER, ElementType.METHOD, ElementType.FIELD]) | ||||
| @interface Global { | ||||
|     String value() | ||||
| } | ||||
| @ -0,0 +1,43 @@ | ||||
| package com.jessebrault.ssg.di | ||||
| 
 | ||||
| import groovy.transform.TupleConstructor | ||||
| import groowt.util.di.Binding | ||||
| import groowt.util.di.QualifierHandler | ||||
| import groowt.util.di.QualifierHandlerContainer | ||||
| import groowt.util.di.RegistryExtension | ||||
| import groowt.util.di.SingletonBinding | ||||
| 
 | ||||
| import java.lang.annotation.Annotation | ||||
| 
 | ||||
| class GlobalsExtension implements QualifierHandlerContainer, RegistryExtension { | ||||
| 
 | ||||
|     @TupleConstructor(includeFields = true) | ||||
|     static class GlobalQualifierHandler implements QualifierHandler<Global> { | ||||
| 
 | ||||
|         private final GlobalsExtension extension | ||||
| 
 | ||||
|         @Override | ||||
|         <T> Binding<T> handle(Global global, Class<T> aClass) { | ||||
|             if (extension.globals.containsKey(global.value())) { | ||||
|                 return new SingletonBinding<T>(extension.globals.get(global.value()) as T) | ||||
|             } else { | ||||
|                 throw new IllegalArgumentException("There is no global for ${global.value()}") | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     final Map<String, Object> globals = [:] | ||||
| 
 | ||||
|     private final GlobalQualifierHandler globalQualifierHandler = new GlobalQualifierHandler(this) | ||||
| 
 | ||||
|     @Override | ||||
|     <A extends Annotation> QualifierHandler<A> getQualifierHandler(Class<A> aClass) { | ||||
|         if (Global.is(aClass)) { | ||||
|             return this.globalQualifierHandler as QualifierHandler<A> | ||||
|         } else { | ||||
|             return null | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,15 @@ | ||||
| package com.jessebrault.ssg.di | ||||
| 
 | ||||
| import jakarta.inject.Qualifier | ||||
| 
 | ||||
| import java.lang.annotation.ElementType | ||||
| import java.lang.annotation.Retention | ||||
| import java.lang.annotation.RetentionPolicy | ||||
| import java.lang.annotation.Target | ||||
| 
 | ||||
| @Qualifier | ||||
| @Retention(RetentionPolicy.RUNTIME) | ||||
| @Target([ElementType.PARAMETER, ElementType.METHOD, ElementType.FIELD]) | ||||
| @interface InjectModel { | ||||
|     String value() | ||||
| } | ||||
| @ -0,0 +1,26 @@ | ||||
| package com.jessebrault.ssg.di | ||||
| 
 | ||||
| import groovy.transform.TupleConstructor | ||||
| import groowt.util.di.Binding | ||||
| import groowt.util.di.QualifierHandler | ||||
| import groowt.util.di.SingletonBinding | ||||
| 
 | ||||
| @TupleConstructor(includeFields = true) | ||||
| class InjectModelQualifierHandler implements QualifierHandler<InjectModel> { | ||||
| 
 | ||||
|     private final ModelsExtension extension | ||||
| 
 | ||||
|     @Override | ||||
|     <T> Binding<T> handle(InjectModel injectModel, Class<T> requestedClass) { | ||||
|         def found = this.extension.allModels.find { | ||||
|             requestedClass.isAssignableFrom(it.class) && it.name == injectModel.value() | ||||
|         } | ||||
|         if (found == null) { | ||||
|             throw new IllegalArgumentException( | ||||
|                     "Could not find a Model with name ${injectModel.value()} and/or type $requestedClass.name" | ||||
|             ) | ||||
|         } | ||||
|         new SingletonBinding<T>(found.get() as T) | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,15 @@ | ||||
| package com.jessebrault.ssg.di | ||||
| 
 | ||||
| import jakarta.inject.Qualifier | ||||
| 
 | ||||
| import java.lang.annotation.ElementType | ||||
| import java.lang.annotation.Retention | ||||
| import java.lang.annotation.RetentionPolicy | ||||
| import java.lang.annotation.Target | ||||
| 
 | ||||
| @Qualifier | ||||
| @Retention(RetentionPolicy.RUNTIME) | ||||
| @Target([ElementType.PARAMETER, ElementType.METHOD, ElementType.FIELD]) | ||||
| @interface InjectModels { | ||||
|     String[] value() | ||||
| } | ||||
| @ -0,0 +1,27 @@ | ||||
| package com.jessebrault.ssg.di | ||||
| 
 | ||||
| import groovy.transform.TupleConstructor | ||||
| import groowt.util.di.Binding | ||||
| import groowt.util.di.QualifierHandler | ||||
| import groowt.util.di.SingletonBinding | ||||
| 
 | ||||
| @TupleConstructor(includeFields = true) | ||||
| class InjectModelsQualifierHandler implements QualifierHandler<InjectModels> { | ||||
| 
 | ||||
|     private final ModelsExtension extension | ||||
| 
 | ||||
|     @Override | ||||
|     <T> Binding<T> handle(InjectModels injectModels, Class<T> requestedType) { | ||||
|         if (!List.is(requestedType)) { | ||||
|             throw new IllegalArgumentException("@InjectModels must be used with List.") | ||||
|         } | ||||
|         def allFound = this.extension.allModels.inject([] as T) { acc, model -> | ||||
|             if (model.type.isAssignableFrom(requestedType) && model.name in injectModels.value()) { | ||||
|                 acc << model.get() | ||||
|             } | ||||
|             acc | ||||
|         } | ||||
|         new SingletonBinding<T>(allFound as T) | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -1,4 +1,4 @@ | ||||
| package com.jessebrault.ssg.objects | ||||
| package com.jessebrault.ssg.di | ||||
| 
 | ||||
| import jakarta.inject.Qualifier | ||||
| 
 | ||||
| @ -1,4 +1,4 @@ | ||||
| package com.jessebrault.ssg.objects | ||||
| package com.jessebrault.ssg.di | ||||
| 
 | ||||
| import com.jessebrault.ssg.page.Page | ||||
| import groovy.transform.TupleConstructor | ||||
| @ -17,7 +17,7 @@ class InjectPageQualifierHandler implements QualifierHandler<InjectPage> { | ||||
|             throw new IllegalArgumentException("Cannot inject a Page into a non-Page parameter/setter/field.") | ||||
|         } | ||||
|         def requested = injectPage.value() | ||||
|         def found = this.pagesExtension.pageProviders.find { | ||||
|         def found = this.pagesExtension.allPages.find { | ||||
|             if (requested.startsWith('/')) { | ||||
|                 it.path == requested | ||||
|             } else { | ||||
| @ -27,7 +27,7 @@ class InjectPageQualifierHandler implements QualifierHandler<InjectPage> { | ||||
|         if (found == null) { | ||||
|             throw new IllegalArgumentException("Cannot find a page with the following name or path: $requested") | ||||
|         } | ||||
|         new SingletonBinding<T>(found.get() as T) | ||||
|         new SingletonBinding<T>(found as T) | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -1,4 +1,4 @@ | ||||
| package com.jessebrault.ssg.objects | ||||
| package com.jessebrault.ssg.di | ||||
| 
 | ||||
| import jakarta.inject.Qualifier | ||||
| 
 | ||||
| @ -1,4 +1,4 @@ | ||||
| package com.jessebrault.ssg.objects | ||||
| package com.jessebrault.ssg.di | ||||
| 
 | ||||
| import com.jessebrault.ssg.page.Page | ||||
| import com.jessebrault.ssg.util.Glob | ||||
| @ -23,19 +23,19 @@ class InjectPagesQualifierHandler implements QualifierHandler<InjectPages> { | ||||
|         for (final String requested : injectPages.value()) { | ||||
|             if (requested.startsWith('/')) { | ||||
|                 def glob = new Glob(requested) | ||||
|                 def allFound = this.pagesExtension.pageProviders.inject([] as Set<Page>) { acc, pageProvider -> | ||||
|                     if (glob.matches(pageProvider.path)) { | ||||
|                         acc << pageProvider.get() | ||||
|                 def allFound = this.pagesExtension.allPages.inject([] as Set<Page>) { acc, page -> | ||||
|                     if (glob.matches(page.path)) { | ||||
|                         acc << page | ||||
|                     } | ||||
|                     acc | ||||
|                 } | ||||
|                 allFound.each { foundPages << it } | ||||
|             } else { | ||||
|                 def found = this.pagesExtension.pageProviders.find { it.name == requested } | ||||
|                 def found = this.pagesExtension.allPages.find { it.name == requested } | ||||
|                 if (found == null) { | ||||
|                     throw new IllegalArgumentException("Cannot find page with the name: $requested") | ||||
|                 } | ||||
|                 foundPages << found.get() | ||||
|                 foundPages << found | ||||
|             } | ||||
|         } | ||||
|         new SingletonBinding<T>(foundPages as T) | ||||
| @ -1,4 +1,4 @@ | ||||
| package com.jessebrault.ssg.objects | ||||
| package com.jessebrault.ssg.di | ||||
| 
 | ||||
| import jakarta.inject.Qualifier | ||||
| 
 | ||||
| @ -0,0 +1,28 @@ | ||||
| package com.jessebrault.ssg.di | ||||
| 
 | ||||
| import com.jessebrault.ssg.text.Text | ||||
| import groovy.transform.TupleConstructor | ||||
| import groowt.util.di.Binding | ||||
| import groowt.util.di.QualifierHandler | ||||
| import groowt.util.di.SingletonBinding | ||||
| 
 | ||||
| @TupleConstructor(includeFields = true) | ||||
| class InjectTextQualifierHandler implements QualifierHandler<InjectText> { | ||||
| 
 | ||||
|     private final TextsExtension extension | ||||
| 
 | ||||
|     @Override | ||||
|     <T> Binding<T> handle(InjectText injectText, Class<T> requestedClass) { | ||||
|         if (!Text.isAssignableFrom(requestedClass)) { | ||||
|             throw new IllegalArgumentException("Cannot @InjectText on a non-Text parameter/method/field.") | ||||
|         } | ||||
|         def found = this.extension.allTexts.find { | ||||
|             it.name == injectText.value() || it.path == injectText.value() | ||||
|         } | ||||
|         if (found == null) { | ||||
|             throw new IllegalArgumentException("Could not find a Text with name or path ${injectText.value()}") | ||||
|         } | ||||
|         new SingletonBinding<T>(found as T) | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -1,4 +1,4 @@ | ||||
| package com.jessebrault.ssg.objects | ||||
| package com.jessebrault.ssg.di | ||||
| 
 | ||||
| import jakarta.inject.Qualifier | ||||
| 
 | ||||
| @ -0,0 +1,39 @@ | ||||
| package com.jessebrault.ssg.di | ||||
| 
 | ||||
| import com.jessebrault.ssg.text.Text | ||||
| import com.jessebrault.ssg.util.Glob | ||||
| import groovy.transform.TupleConstructor | ||||
| import groowt.util.di.Binding | ||||
| import groowt.util.di.QualifierHandler | ||||
| import groowt.util.di.SingletonBinding | ||||
| 
 | ||||
| @TupleConstructor(includeFields = true) | ||||
| class InjectTextsQualifierHandler implements QualifierHandler<InjectTexts> { | ||||
| 
 | ||||
|     private final TextsExtension extension | ||||
| 
 | ||||
|     @Override | ||||
|     <T> Binding<T> handle(InjectTexts injectTexts, Class<T> aClass) { | ||||
|         if (!Set.is(aClass)) { | ||||
|             throw new IllegalArgumentException('Cannot @InjectTexts on a non-Set parameter/method/field.') | ||||
|         } | ||||
|         def allFound = injectTexts.value().inject([] as Set<Text>) { acc, nameOrPathGlob -> | ||||
|             if (nameOrPathGlob.startsWith('/')) { | ||||
|                 def glob = new Glob(nameOrPathGlob) | ||||
|                 def matching = this.extension.allTexts.inject([] as Set<Text>) { matchingAcc, text -> | ||||
|                     if (glob.matches(text.path)) { | ||||
|                         matchingAcc << text | ||||
|                     } | ||||
|                     matchingAcc | ||||
|                 } | ||||
|                 acc.addAll(matching) | ||||
|             } else { | ||||
|                 def found = this.extension.allTexts.find { it.name == nameOrPathGlob } | ||||
|                 acc << found | ||||
|             } | ||||
|             acc | ||||
|         } | ||||
|         new SingletonBinding<T>(allFound as T) | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,28 @@ | ||||
| package com.jessebrault.ssg.di | ||||
| 
 | ||||
| import com.jessebrault.ssg.model.Model | ||||
| import groowt.util.di.QualifierHandler | ||||
| import groowt.util.di.QualifierHandlerContainer | ||||
| import groowt.util.di.RegistryExtension | ||||
| 
 | ||||
| import java.lang.annotation.Annotation | ||||
| 
 | ||||
| class ModelsExtension implements QualifierHandlerContainer, RegistryExtension { | ||||
| 
 | ||||
|     final Set<Model> allModels = [] | ||||
| 
 | ||||
|     private final QualifierHandler<InjectModel> injectModelQualifierHandler = new InjectModelQualifierHandler(this) | ||||
|     private final QualifierHandler<InjectModels> injectModelsQualifierHandler = new InjectModelsQualifierHandler(this) | ||||
| 
 | ||||
|     @Override | ||||
|     <A extends Annotation> QualifierHandler<A> getQualifierHandler(Class<A> aClass) { | ||||
|         if (aClass == InjectModel) { | ||||
|             return this.injectModelQualifierHandler as QualifierHandler<A> | ||||
|         } else if (aClass == InjectModels) { | ||||
|             return this.injectModelsQualifierHandler as QualifierHandler<A> | ||||
|         } else { | ||||
|             return null | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -1,7 +1,6 @@ | ||||
| package com.jessebrault.ssg.objects | ||||
| package com.jessebrault.ssg.di | ||||
| 
 | ||||
| 
 | ||||
| import com.jessebrault.ssg.provider.PageProvider | ||||
| import com.jessebrault.ssg.page.Page | ||||
| import groowt.util.di.QualifierHandler | ||||
| import groowt.util.di.QualifierHandlerContainer | ||||
| import groowt.util.di.RegistryExtension | ||||
| @ -10,7 +9,7 @@ import java.lang.annotation.Annotation | ||||
| 
 | ||||
| class PagesExtension implements QualifierHandlerContainer, RegistryExtension { | ||||
| 
 | ||||
|     final Set<PageProvider> pageProviders = [] | ||||
|     final Set<Page> allPages = [] | ||||
| 
 | ||||
|     private final QualifierHandler<InjectPage> injectPage = new InjectPageQualifierHandler(this) | ||||
|     private final QualifierHandler<InjectPages> injectPages = new InjectPagesQualifierHandler(this) | ||||
							
								
								
									
										13
									
								
								api/src/main/groovy/com/jessebrault/ssg/di/SelfPage.groovy
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								api/src/main/groovy/com/jessebrault/ssg/di/SelfPage.groovy
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,13 @@ | ||||
| package com.jessebrault.ssg.di | ||||
| 
 | ||||
| import jakarta.inject.Qualifier | ||||
| 
 | ||||
| import java.lang.annotation.ElementType | ||||
| import java.lang.annotation.Retention | ||||
| import java.lang.annotation.RetentionPolicy | ||||
| import java.lang.annotation.Target | ||||
| 
 | ||||
| @Qualifier | ||||
| @Retention(RetentionPolicy.RUNTIME) | ||||
| @Target([ElementType.PARAMETER, ElementType.METHOD, ElementType.FIELD]) | ||||
| @interface SelfPage {} | ||||
| @ -0,0 +1,42 @@ | ||||
| package com.jessebrault.ssg.di | ||||
| 
 | ||||
| import com.jessebrault.ssg.page.Page | ||||
| import groovy.transform.TupleConstructor | ||||
| import groowt.util.di.* | ||||
| 
 | ||||
| import java.lang.annotation.Annotation | ||||
| 
 | ||||
| class SelfPageExtension implements RegistryExtension, QualifierHandlerContainer { | ||||
| 
 | ||||
|     @TupleConstructor(includeFields = true) | ||||
|     static class SelfPageQualifierHandler implements QualifierHandler<SelfPage> { | ||||
| 
 | ||||
|         private final SelfPageExtension extension | ||||
| 
 | ||||
|         @Override | ||||
|         <T> Binding<T> handle(SelfPage selfPage, Class<T> requestedType) { | ||||
|             if (!Page.class.isAssignableFrom(requestedType)) { | ||||
|                 throw new IllegalArgumentException('Cannot put @SelfPage on a non-Page parameter/method/field.') | ||||
|             } | ||||
|             if (this.extension.currentPage == null) { | ||||
|                 throw new IllegalStateException('Cannot get @SelfPage because extension.currentPage is null.') | ||||
|             } | ||||
|             new SingletonBinding<T>(this.extension.currentPage as T) | ||||
|         } | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     Page currentPage | ||||
| 
 | ||||
|     private final SelfPageQualifierHandler selfPageQualifierHandler = new SelfPageQualifierHandler(this) | ||||
| 
 | ||||
|     @Override | ||||
|     <A extends Annotation> QualifierHandler<A> getQualifierHandler(Class<A> annotationType) { | ||||
|         if (SelfPage.is(annotationType)) { | ||||
|             return this.selfPageQualifierHandler as QualifierHandler<A> | ||||
|         } else { | ||||
|             return null | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -1,4 +1,4 @@ | ||||
| package com.jessebrault.ssg.objects | ||||
| package com.jessebrault.ssg.di | ||||
| 
 | ||||
| import groowt.util.di.DefaultRegistryObjectFactory | ||||
| import groowt.util.di.RegistryObjectFactory | ||||
| @ -0,0 +1,28 @@ | ||||
| package com.jessebrault.ssg.di | ||||
| 
 | ||||
| import com.jessebrault.ssg.text.Text | ||||
| import groowt.util.di.QualifierHandler | ||||
| import groowt.util.di.QualifierHandlerContainer | ||||
| import groowt.util.di.RegistryExtension | ||||
| 
 | ||||
| import java.lang.annotation.Annotation | ||||
| 
 | ||||
| class TextsExtension implements QualifierHandlerContainer, RegistryExtension { | ||||
| 
 | ||||
|     final Set<Text> allTexts = [] | ||||
| 
 | ||||
|     private final QualifierHandler<InjectText> injectTextQualifierHandler = new InjectTextQualifierHandler(this) | ||||
|     private final QualifierHandler<InjectTexts> injectTextsQualifierHandler = new InjectTextsQualifierHandler(this) | ||||
| 
 | ||||
|     @Override | ||||
|     <A extends Annotation> QualifierHandler<A> getQualifierHandler(Class<A> aClass) { | ||||
|         if (InjectText.is(aClass)) { | ||||
|             return this.injectTextQualifierHandler as QualifierHandler<A> | ||||
|         } else if (InjectTexts.is(aClass)) { | ||||
|             return this.injectTextsQualifierHandler as QualifierHandler<A> | ||||
|         } else { | ||||
|             return null | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -1,56 +0,0 @@ | ||||
| package com.jessebrault.ssg.dsl | ||||
| 
 | ||||
| import com.jessebrault.ssg.part.Part | ||||
| import com.jessebrault.ssg.render.RenderContext | ||||
| import com.jessebrault.ssg.text.Text | ||||
| import com.jessebrault.ssg.util.Diagnostic | ||||
| import groovy.transform.EqualsAndHashCode | ||||
| import org.jetbrains.annotations.Nullable | ||||
| 
 | ||||
| import java.util.function.Consumer | ||||
| 
 | ||||
| import static java.util.Objects.requireNonNull | ||||
| 
 | ||||
| @EqualsAndHashCode(includeFields = true) | ||||
| final class EmbeddablePart { | ||||
| 
 | ||||
|     private final Part part | ||||
|     private final RenderContext context | ||||
|     private final Consumer<Collection<Diagnostic>> diagnosticsConsumer | ||||
| 
 | ||||
|     @Nullable | ||||
|     private final Text text | ||||
| 
 | ||||
|     EmbeddablePart( | ||||
|             Part part, | ||||
|             RenderContext context, | ||||
|             Consumer<Collection<Diagnostic>> diagnosticsConsumer, | ||||
|             @Nullable Text text | ||||
|     ) { | ||||
|         this.part = requireNonNull(part) | ||||
|         this.context = requireNonNull(context) | ||||
|         this.diagnosticsConsumer = requireNonNull(diagnosticsConsumer) | ||||
|         this.text = text | ||||
|     } | ||||
| 
 | ||||
|     String render(Map binding = [:]) { | ||||
|         def result = this.part.type.renderer.render( | ||||
|                 this.part, | ||||
|                 binding, | ||||
|                 this.context, | ||||
|                 this.text | ||||
|         ) | ||||
|         if (result.hasDiagnostics()) { | ||||
|             this.diagnosticsConsumer.accept(result.diagnostics) | ||||
|             '' | ||||
|         } else { | ||||
|             result.get() | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     String toString() { | ||||
|         "EmbeddablePart(part: ${ this.part })" | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -1,36 +0,0 @@ | ||||
| package com.jessebrault.ssg.dsl | ||||
| 
 | ||||
| import com.jessebrault.ssg.render.RenderContext | ||||
| import com.jessebrault.ssg.text.Text | ||||
| import com.jessebrault.ssg.util.Diagnostic | ||||
| import groovy.transform.EqualsAndHashCode | ||||
| import org.jetbrains.annotations.Nullable | ||||
| 
 | ||||
| import java.util.function.Consumer | ||||
| 
 | ||||
| import static java.util.Objects.requireNonNull | ||||
| 
 | ||||
| @EqualsAndHashCode(includeFields = true) | ||||
| final class EmbeddablePartsMap { | ||||
| 
 | ||||
|     @Delegate | ||||
|     private final Map<String, EmbeddablePart> partsMap = [:] | ||||
| 
 | ||||
|     EmbeddablePartsMap( | ||||
|             RenderContext context, | ||||
|             Consumer<Collection<Diagnostic>> diagnosticsConsumer, | ||||
|             @Nullable Text text = null | ||||
|     ) { | ||||
|         requireNonNull(context) | ||||
|         requireNonNull(diagnosticsConsumer) | ||||
|         context.parts.each { | ||||
|             this[it.path] = new EmbeddablePart(it, context, diagnosticsConsumer, text) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     String toString() { | ||||
|         "EmbeddablePartsMap(partsMap: ${ this.partsMap })" | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -1,63 +0,0 @@ | ||||
| package com.jessebrault.ssg.dsl | ||||
| 
 | ||||
| import com.jessebrault.ssg.text.FrontMatter | ||||
| import com.jessebrault.ssg.text.Text | ||||
| import com.jessebrault.ssg.util.Diagnostic | ||||
| import groovy.transform.EqualsAndHashCode | ||||
| import groovy.transform.Memoized | ||||
| import groovy.transform.NullCheck | ||||
| import groovy.transform.TupleConstructor | ||||
| 
 | ||||
| import java.util.function.Consumer | ||||
| 
 | ||||
| @TupleConstructor(includeFields = true, defaults = false) | ||||
| @NullCheck(includeGenerated = true) | ||||
| @EqualsAndHashCode(includeFields = true) | ||||
| final class EmbeddableText { | ||||
| 
 | ||||
|     private final Text text | ||||
|     private final Consumer<Collection<Diagnostic>> diagnosticsConsumer | ||||
| 
 | ||||
|     @Memoized | ||||
|     String render() { | ||||
|         def result = this.text.type.renderer.render(this.text) | ||||
|         if (result.diagnostics.size() > 0) { | ||||
|             this.diagnosticsConsumer.accept(result.diagnostics) | ||||
|             '' | ||||
|         } else { | ||||
|             result.get() | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @Memoized | ||||
|     FrontMatter getFrontMatter() { | ||||
|         def result = this.text.type.frontMatterGetter.get(this.text) | ||||
|         if (result.hasDiagnostics()) { | ||||
|             this.diagnosticsConsumer.accept(result.diagnostics) | ||||
|             new FrontMatter(this.text, [:]) | ||||
|         } else { | ||||
|             result.get() | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @Memoized | ||||
|     String getExcerpt(int limit) { | ||||
|         def result = this.text.type.excerptGetter.getExcerpt(this.text, limit) | ||||
|         if (result.hasDiagnostics()) { | ||||
|             this.diagnosticsConsumer.accept(result.diagnostics) | ||||
|             '' | ||||
|         } else { | ||||
|             result.get() | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     String getPath() { | ||||
|         this.text.path | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     String toString() { | ||||
|         "EmbeddableText(text: ${ this.text })" | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -1,28 +0,0 @@ | ||||
| package com.jessebrault.ssg.dsl | ||||
| 
 | ||||
| import com.jessebrault.ssg.text.Text | ||||
| import com.jessebrault.ssg.util.Diagnostic | ||||
| import groovy.transform.EqualsAndHashCode | ||||
| import groovy.transform.NullCheck | ||||
| 
 | ||||
| import java.util.function.Consumer | ||||
| 
 | ||||
| @NullCheck | ||||
| @EqualsAndHashCode(includeFields = true) | ||||
| final class EmbeddableTextsCollection { | ||||
| 
 | ||||
|     @Delegate | ||||
|     private final Collection<EmbeddableText> embeddableTexts = [] | ||||
| 
 | ||||
|     EmbeddableTextsCollection(Collection<Text> texts, Consumer<Collection<Diagnostic>> diagnosticsConsumer) { | ||||
|         texts.each { | ||||
|             this << new EmbeddableText(it, diagnosticsConsumer) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     String toString() { | ||||
|         "EmbeddableTextsCollection(embeddableTexts: ${ this.embeddableTexts })" | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -1,84 +0,0 @@ | ||||
| package com.jessebrault.ssg.dsl | ||||
| 
 | ||||
| import com.jessebrault.ssg.model.Model | ||||
| import groovy.transform.EqualsAndHashCode | ||||
| import groovy.transform.NullCheck | ||||
| import org.jetbrains.annotations.Nullable | ||||
| import org.slf4j.Logger | ||||
| import org.slf4j.LoggerFactory | ||||
| import org.slf4j.Marker | ||||
| import org.slf4j.MarkerFactory | ||||
| 
 | ||||
| import java.util.function.Predicate | ||||
| 
 | ||||
| @NullCheck | ||||
| @EqualsAndHashCode(includeFields = true) | ||||
| final class ModelCollection<T> { | ||||
| 
 | ||||
|     private static final Logger logger = LoggerFactory.getLogger(ModelCollection) | ||||
|     private static final Marker enter = MarkerFactory.getMarker('ENTER') | ||||
|     private static final Marker exit = MarkerFactory.getMarker('EXIT') | ||||
| 
 | ||||
|     @Delegate | ||||
|     private final Collection<Model<T>> models = [] | ||||
| 
 | ||||
|     ModelCollection(Collection<Model<T>> models) { | ||||
|         this.models.addAll(models) | ||||
|     } | ||||
| 
 | ||||
|     @Nullable | ||||
|     Model<T> getByName(String name) { | ||||
|         this.models.find { it.name == name } | ||||
|     } | ||||
| 
 | ||||
|     @Nullable | ||||
|     <E extends T> Model<E> getByNameAndType(String name, Class<E> type) { | ||||
|         this.models.find { it.name == name && type.isAssignableFrom(it.get().class) } as Model<E> | ||||
|     } | ||||
| 
 | ||||
|     Optional<Model<T>> findByName(String name) { | ||||
|         Optional.ofNullable(this.getByName(name)) | ||||
|     } | ||||
| 
 | ||||
|     def <E extends T> Optional<Model<E>> findByNameAndType(String name, Class<E> type) { | ||||
|         Optional.ofNullable(this.getByNameAndType(name, type)) | ||||
|     } | ||||
| 
 | ||||
|     def <E> ModelCollection<E> findAllByType(Class<E> type) { | ||||
|         logger.trace(enter, 'type: {}', type) | ||||
|         def es = this.models.findResults { | ||||
|             def itType = it.get().class | ||||
|             def itResult = type.isAssignableFrom(itType) | ||||
|             logger.debug( | ||||
|                     'it: {}, itType: {}, itType.classLoader: {}, itResult: {}', | ||||
|                     it, itType, itType.classLoader, itResult | ||||
|             ) | ||||
|             itResult ? it as Model<E> : null | ||||
|         } | ||||
|         def result = new ModelCollection<>(es) | ||||
|         logger.trace(exit, 'result: {}', result) | ||||
|         result | ||||
|     } | ||||
| 
 | ||||
|     def <E extends T> Optional<Model<E>> findOne(Class<E> type, Predicate<E> filter) { | ||||
|         Optional.ofNullable(this.models.find { | ||||
|             def t = it.get() | ||||
|             if (type.isAssignableFrom(t.class)) { | ||||
|                 filter.test(t as E) ? it : null | ||||
|             } else { | ||||
|                 null | ||||
|             } | ||||
|         } as Model<E>) | ||||
|     } | ||||
| 
 | ||||
|     def <E extends T> Optional<Model<E>> findOne(Class<E> type) { | ||||
|         this.findOne(type) { true } | ||||
|     } | ||||
| 
 | ||||
|     Optional<Model<T>> findOne(Predicate<T> filter) { | ||||
|         Optional.ofNullable(this.models.find { | ||||
|             filter.test(it.get()) ? it : null | ||||
|         }) | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -1,81 +0,0 @@ | ||||
| package com.jessebrault.ssg.dsl | ||||
| 
 | ||||
| import com.jessebrault.ssg.dsl.tagbuilder.DynamicTagBuilder | ||||
| import com.jessebrault.ssg.dsl.urlbuilder.PathBasedUrlBuilder | ||||
| import com.jessebrault.ssg.render.RenderContext | ||||
| import com.jessebrault.ssg.text.Text | ||||
| import com.jessebrault.ssg.util.Diagnostic | ||||
| import groovy.transform.NullCheck | ||||
| import org.slf4j.LoggerFactory | ||||
| 
 | ||||
| import java.util.function.Consumer | ||||
| 
 | ||||
| final class StandardDslMap { | ||||
| 
 | ||||
|     @NullCheck(includeGenerated = true) | ||||
|     static final class Builder { | ||||
| 
 | ||||
|         private final Map<String, Object> custom = [:] | ||||
| 
 | ||||
|         Consumer<Collection<Diagnostic>> diagnosticsConsumer = { } | ||||
|         String loggerName = '' | ||||
|         Text text = null | ||||
| 
 | ||||
|         void putCustom(String key, Object value) { | ||||
|             this.custom.put(key, value) | ||||
|         } | ||||
| 
 | ||||
|         void putAllCustom(Map<String, Object> m) { | ||||
|             this.custom.putAll(m) | ||||
|         } | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     static Map<String, Object> get( | ||||
|             RenderContext context, | ||||
|             Consumer<Builder> builderConsumer | ||||
|     ) { | ||||
|         def b = new Builder() | ||||
|         builderConsumer.accept(b) | ||||
| 
 | ||||
|         [:].tap { | ||||
|             // standard variables | ||||
|             it.globals = context.globals | ||||
|             it.logger = LoggerFactory.getLogger(b.loggerName) | ||||
|             it.models = new ModelCollection<Object>(context.models) | ||||
|             it.parts = new EmbeddablePartsMap( | ||||
|                     context, | ||||
|                     b.diagnosticsConsumer, | ||||
|                     b.text | ||||
|             ) | ||||
|             it.siteSpec = context.siteSpec | ||||
|             it.sourcePath = context.sourcePath | ||||
|             it.tagBuilder = new DynamicTagBuilder() | ||||
|             it.targetPath = context.targetPath | ||||
|             it.tasks = new TaskCollection(context.tasks) | ||||
|             it.text = b.text ? new EmbeddableText( | ||||
|                     b.text, | ||||
|                     b.diagnosticsConsumer | ||||
|             ) : null | ||||
|             it.texts = new EmbeddableTextsCollection( | ||||
|                     context.texts, | ||||
|                     b.diagnosticsConsumer | ||||
|             ) | ||||
|             it.urlBuilder = new PathBasedUrlBuilder( | ||||
|                     context.targetPath, | ||||
|                     context.siteSpec.baseUrl | ||||
|             ) | ||||
| 
 | ||||
|             // task types | ||||
|             it.Task = com.jessebrault.ssg.task.Task | ||||
|             it.HtmlTask = com.jessebrault.ssg.html.HtmlTask | ||||
|             it.ModelToHtmlTask = com.jessebrault.ssg.html.ModelToHtmlTask | ||||
|             it.PageToHtmlTask = com.jessebrault.ssg.html.PageToHtmlTask | ||||
|             it.TextToHtmlTask = com.jessebrault.ssg.html.TextToHtmlTask | ||||
| 
 | ||||
|             // custom | ||||
|             it.putAll(b.custom) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -1,21 +0,0 @@ | ||||
| package com.jessebrault.ssg.dsl | ||||
| 
 | ||||
| import com.jessebrault.ssg.task.Task | ||||
| import groovy.transform.EqualsAndHashCode | ||||
| 
 | ||||
| @EqualsAndHashCode(includeFields = true) | ||||
| final class TaskCollection { | ||||
| 
 | ||||
|     @Delegate | ||||
|     private final Collection<Task> tasks | ||||
| 
 | ||||
|     TaskCollection(Collection<? extends Task> src = []) { | ||||
|         this.tasks = [] | ||||
|         this.tasks.addAll(src) | ||||
|     } | ||||
| 
 | ||||
|     def <T extends Task> Collection<T> byType(Class<T> taskClass) { | ||||
|         this.tasks.findAll { taskClass.isAssignableFrom(it.class) } as Collection<T> | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -1,77 +0,0 @@ | ||||
| package com.jessebrault.ssg.dsl.tagbuilder | ||||
| 
 | ||||
| import org.codehaus.groovy.runtime.InvokerHelper | ||||
| 
 | ||||
| final class DynamicTagBuilder implements TagBuilder { | ||||
| 
 | ||||
|     @Override | ||||
|     String create(String name) { | ||||
|         "<$name />" | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     String create(String name, Map<String, Object> attributes) { | ||||
|         def formattedAttributes = attributes.collect { | ||||
|             if (it.value instanceof String) { | ||||
|                 it.key + '="' + it.value + '"' | ||||
|             } else if (it.value instanceof Integer) { | ||||
|                 it.key + '=' + it.value | ||||
|             } else if (it.value instanceof Boolean && it.value == true) { | ||||
|                 it.key | ||||
|             } else { | ||||
|                 it.key + '="' + it.value.toString() + '"' | ||||
|             } | ||||
|         }.join(' ') | ||||
|         "<$name $formattedAttributes />" | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     String create(String name, String body) { | ||||
|         "<$name>$body</$name>" | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     String create(String name, Map<String, Object> attributes, String body) { | ||||
|         def formattedAttributes = attributes.collect { | ||||
|             if (it.value instanceof String) { | ||||
|                 it.key + '="' + it.value + '"' | ||||
|             } else if (it.value instanceof Integer) { | ||||
|                 it.key + '=' + it.value | ||||
|             } else if (it.value instanceof Boolean && it.value == true) { | ||||
|                 it.key | ||||
|             } else { | ||||
|                 it.key + '="' + it.value.toString() + '"' | ||||
|             } | ||||
|         }.join(' ') | ||||
|         "<$name $formattedAttributes>$body</$name>" | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     Object invokeMethod(String name, Object args) { | ||||
|         def argsList = InvokerHelper.asList(args) | ||||
|         return switch (argsList.size()) { | ||||
|             case 0 -> this.create(name) | ||||
|             case 1 -> { | ||||
|                 def arg0 = argsList[0] | ||||
|                 if (arg0 instanceof Map) { | ||||
|                     this.create(name, arg0) | ||||
|                 } else if (arg0 instanceof String) { | ||||
|                     this.create(name, arg0) | ||||
|                 } else { | ||||
|                     throw new MissingMethodException(name, this.class, args, false) | ||||
|                 } | ||||
|             } | ||||
|             case 2 -> { | ||||
|                 def arg0 = argsList[0] | ||||
|                 def arg1 = argsList[1] | ||||
|                 if (arg0 instanceof Map && arg1 instanceof String) { | ||||
|                     this.create(name, arg0, arg1) | ||||
|                 } else { | ||||
|                     throw new MissingMethodException(name, this.class, args, false) | ||||
|                 } | ||||
|             } | ||||
|             default -> throw new MissingMethodException(name, this.class, args, false) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -1,8 +0,0 @@ | ||||
| package com.jessebrault.ssg.dsl.tagbuilder | ||||
| 
 | ||||
| interface TagBuilder { | ||||
|     String create(String name) | ||||
|     String create(String name, Map<String, Object> attributes) | ||||
|     String create(String name, String body) | ||||
|     String create(String name, Map<String, Object> attributes, String body) | ||||
| } | ||||
| @ -1,59 +0,0 @@ | ||||
| package com.jessebrault.ssg.html | ||||
| 
 | ||||
| import com.jessebrault.ssg.task.AbstractTask | ||||
| import com.jessebrault.ssg.task.Task | ||||
| import com.jessebrault.ssg.task.TaskInput | ||||
| import com.jessebrault.ssg.util.Diagnostic | ||||
| import com.jessebrault.ssg.util.Result | ||||
| import groovy.transform.EqualsAndHashCode | ||||
| import groovy.transform.NullCheck | ||||
| import org.jsoup.Jsoup | ||||
| 
 | ||||
| @NullCheck | ||||
| @EqualsAndHashCode | ||||
| abstract class AbstractHtmlTask<I extends TaskInput> extends AbstractTask implements HtmlTask { | ||||
| 
 | ||||
|     final String htmlPath | ||||
|     final I input | ||||
|     final HtmlOutput output | ||||
| 
 | ||||
|     AbstractHtmlTask( | ||||
|             String name, | ||||
|             String htmlPath, | ||||
|             I input, | ||||
|             File buildDir | ||||
|     ) { | ||||
|         super(name) | ||||
|         this.htmlPath = htmlPath | ||||
|         this.input = input | ||||
|         this.output = new SimpleHtmlOutput( | ||||
|                 "htmlOutput:${ htmlPath }", | ||||
|                 new File(buildDir, htmlPath), | ||||
|                 htmlPath | ||||
|         ) | ||||
|     } | ||||
| 
 | ||||
|     protected abstract Result<String> transform(Collection<Task> allTasks) | ||||
| 
 | ||||
|     @Override | ||||
|     final Collection<Diagnostic> execute(Collection<Task> allTasks) { | ||||
|         def transformResult = this.transform(allTasks) | ||||
|         if (transformResult.hasDiagnostics()) { | ||||
|             transformResult.diagnostics | ||||
|         } else { | ||||
|             def content = transformResult.get() | ||||
|             def document = Jsoup.parse(content) | ||||
|             document.outputSettings().indentAmount(4) | ||||
|             def formatted = document.toString() | ||||
|             this.output.file.createParentDirectories() | ||||
|             this.output.file.write(formatted) | ||||
|             [] | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     String toString() { | ||||
|         "AbstractHtmlTask(path: ${ this.htmlPath }, super: ${ super.toString() })" | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -1,7 +0,0 @@ | ||||
| package com.jessebrault.ssg.html | ||||
| 
 | ||||
| import com.jessebrault.ssg.task.FileOutput | ||||
| 
 | ||||
| interface HtmlOutput extends FileOutput { | ||||
|     String getHtmlPath() | ||||
| } | ||||
| @ -1,12 +0,0 @@ | ||||
| package com.jessebrault.ssg.html | ||||
| 
 | ||||
| import com.jessebrault.ssg.task.Task | ||||
| import com.jessebrault.ssg.task.TaskInput | ||||
| 
 | ||||
| interface HtmlTask extends Task { | ||||
|     @Deprecated | ||||
|     String getHtmlPath() | ||||
| 
 | ||||
|     TaskInput getInput() | ||||
|     HtmlOutput getOutput() | ||||
| } | ||||
| @ -1,16 +0,0 @@ | ||||
| package com.jessebrault.ssg.html | ||||
| 
 | ||||
| import com.jessebrault.ssg.model.Model | ||||
| import com.jessebrault.ssg.template.Template | ||||
| import groovy.transform.EqualsAndHashCode | ||||
| import groovy.transform.NullCheck | ||||
| import groovy.transform.TupleConstructor | ||||
| 
 | ||||
| @TupleConstructor(defaults = false) | ||||
| @NullCheck(includeGenerated = true) | ||||
| @EqualsAndHashCode | ||||
| final class ModelToHtmlSpec<T> { | ||||
|     final Model<T> model | ||||
|     final Template template | ||||
|     final String path | ||||
| } | ||||
| @ -1,71 +0,0 @@ | ||||
| package com.jessebrault.ssg.html | ||||
| 
 | ||||
| import com.jessebrault.ssg.SiteSpec | ||||
| import com.jessebrault.ssg.model.Model | ||||
| import com.jessebrault.ssg.model.ModelInput | ||||
| import com.jessebrault.ssg.part.Part | ||||
| import com.jessebrault.ssg.render.RenderContext | ||||
| import com.jessebrault.ssg.task.Task | ||||
| import com.jessebrault.ssg.task.TaskSpec | ||||
| import com.jessebrault.ssg.template.Template | ||||
| import com.jessebrault.ssg.text.Text | ||||
| import com.jessebrault.ssg.util.Result | ||||
| import groovy.transform.EqualsAndHashCode | ||||
| import groovy.transform.NullCheck | ||||
| 
 | ||||
| @NullCheck | ||||
| @EqualsAndHashCode | ||||
| final class ModelToHtmlTask<T> extends AbstractHtmlTask<ModelInput<T>> { | ||||
| 
 | ||||
|     private final SiteSpec siteSpec | ||||
|     private final Map<String, Object> globals | ||||
|     private final Model<T> model | ||||
|     private final Template template | ||||
|     private final Collection<Text> allTexts | ||||
|     private final Collection<Model<Object>> allModels | ||||
|     private final Collection<Part> allParts | ||||
| 
 | ||||
|     ModelToHtmlTask( | ||||
|             String relativeHtmlPath, | ||||
|             TaskSpec taskSpec, | ||||
|             Model<T> model, | ||||
|             Template template, | ||||
|             Collection<Text> allTexts, | ||||
|             Collection<Model<Object>> allModels, | ||||
|             Collection<Part> allParts | ||||
|     ) { | ||||
|         super( | ||||
|                 "modelToHtml:${ relativeHtmlPath }", | ||||
|                 relativeHtmlPath, | ||||
|                 new ModelInput<>(model.name, model), | ||||
|                 taskSpec.outputDir | ||||
|         ) | ||||
|         this.siteSpec = taskSpec.siteSpec | ||||
|         this.globals = taskSpec.globals | ||||
|         this.model = model | ||||
|         this.template = template | ||||
|         this.allTexts = allTexts | ||||
|         this.allModels = allModels | ||||
|         this.allParts = allParts | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     protected Result<String> transform(Collection<Task> allTasks) { | ||||
|         this.template.type.renderer.render(this.template, null, new RenderContext( | ||||
|                 sourcePath: this.model.name, | ||||
|                 targetPath: this.htmlPath, | ||||
|                 tasks: allTasks, | ||||
|                 texts: this.allTexts, | ||||
|                 models: this.allModels, | ||||
|                 parts: this.allParts, | ||||
|                 siteSpec: this.siteSpec, | ||||
|                 globals: this.globals | ||||
|         )) | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     String toString() { | ||||
|         "ModelToHtml(${ this.model }, ${ this.template }, ${ this.allTexts }, ${ this.allParts }, ${ super.toString() })" | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -1,33 +0,0 @@ | ||||
| package com.jessebrault.ssg.html | ||||
| 
 | ||||
| import com.jessebrault.ssg.provider.CollectionProvider | ||||
| import com.jessebrault.ssg.provider.CollectionProviders | ||||
| import com.jessebrault.ssg.task.AbstractRenderTaskFactory | ||||
| import com.jessebrault.ssg.task.Task | ||||
| import com.jessebrault.ssg.task.TaskSpec | ||||
| import com.jessebrault.ssg.util.Result | ||||
| 
 | ||||
| final class ModelToHtmlTaskFactory<T> extends AbstractRenderTaskFactory { | ||||
| 
 | ||||
|     CollectionProvider<ModelToHtmlSpec<T>> specsProvider = CollectionProviders.getEmpty() | ||||
| 
 | ||||
|     @Override | ||||
|     Result<Collection<Task>> getTasks(TaskSpec taskSpec) { | ||||
|         def allTexts = this.allTextsProvider.provide() | ||||
|         def allModels = this.allModelsProvider.provide() | ||||
|         def allParts = this.allPartsProvider.provide() | ||||
| 
 | ||||
|         Result.of(specsProvider.provide().collect { | ||||
|             new ModelToHtmlTask<>( | ||||
|                     it.path, | ||||
|                     taskSpec, | ||||
|                     it.model, | ||||
|                     it.template, | ||||
|                     allTexts, | ||||
|                     allModels, | ||||
|                     allParts | ||||
|             ) | ||||
|         }) | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -1,17 +0,0 @@ | ||||
| package com.jessebrault.ssg.html | ||||
| 
 | ||||
| import com.jessebrault.ssg.page.Page | ||||
| import com.jessebrault.ssg.task.TaskSpec | ||||
| import groovy.transform.EqualsAndHashCode | ||||
| import groovy.transform.NullCheck | ||||
| import groovy.transform.TupleConstructor | ||||
| 
 | ||||
| import java.util.function.Function | ||||
| 
 | ||||
| @TupleConstructor(defaults = false) | ||||
| @NullCheck(includeGenerated = true) | ||||
| @EqualsAndHashCode | ||||
| final class PageToHtmlSpec { | ||||
|     final Page page | ||||
|     final Function<TaskSpec, String> toRelativeHtmlPath | ||||
| } | ||||
| @ -1,34 +0,0 @@ | ||||
| package com.jessebrault.ssg.html | ||||
| 
 | ||||
| import com.jessebrault.ssg.page.Page | ||||
| import com.jessebrault.ssg.provider.CollectionProvider | ||||
| import com.jessebrault.ssg.provider.CollectionProviders | ||||
| import com.jessebrault.ssg.task.TaskSpec | ||||
| import com.jessebrault.ssg.util.ExtensionUtil | ||||
| import groovy.transform.NullCheck | ||||
| 
 | ||||
| import java.util.function.Function | ||||
| 
 | ||||
| @NullCheck | ||||
| final class PageToHtmlSpecProviders { | ||||
| 
 | ||||
|     static CollectionProvider<PageToHtmlSpec> from(CollectionProvider<Page> pagesProvider) { | ||||
|         CollectionProviders.fromCollection(pagesProvider.provide().collect { Page page -> | ||||
|             new PageToHtmlSpec(page, { TaskSpec taskSpec -> | ||||
|                 ExtensionUtil.stripExtension(page.path) + '.html' | ||||
|             }) | ||||
|         }) | ||||
|     } | ||||
| 
 | ||||
|     static CollectionProvider<PageToHtmlSpec> from( | ||||
|             CollectionProvider<Page> pagesProvider, | ||||
|             Function<Page, Function<TaskSpec, String>> toRelativeHtmlPath | ||||
|     ) { | ||||
|         CollectionProviders.fromCollection(pagesProvider.provide().collect { | ||||
|             new PageToHtmlSpec(it, toRelativeHtmlPath.apply(it)) | ||||
|         }) | ||||
|     } | ||||
| 
 | ||||
|     private PageToHtmlSpecProviders() {} | ||||
| 
 | ||||
| } | ||||
| @ -1,68 +0,0 @@ | ||||
| package com.jessebrault.ssg.html | ||||
| 
 | ||||
| import com.jessebrault.ssg.SiteSpec | ||||
| import com.jessebrault.ssg.model.Model | ||||
| import com.jessebrault.ssg.page.Page | ||||
| import com.jessebrault.ssg.page.PageInput | ||||
| import com.jessebrault.ssg.part.Part | ||||
| import com.jessebrault.ssg.render.RenderContext | ||||
| import com.jessebrault.ssg.task.Task | ||||
| import com.jessebrault.ssg.task.TaskSpec | ||||
| import com.jessebrault.ssg.text.Text | ||||
| import com.jessebrault.ssg.util.Result | ||||
| import groovy.transform.EqualsAndHashCode | ||||
| import groovy.transform.NullCheck | ||||
| 
 | ||||
| @NullCheck | ||||
| @EqualsAndHashCode(includeFields = true, callSuper = true) | ||||
| final class PageToHtmlTask extends AbstractHtmlTask<PageInput> { | ||||
| 
 | ||||
|     private final SiteSpec siteSpec | ||||
|     private final Map<String, Object> globals | ||||
|     private final Page page | ||||
|     private final Collection<Text> allTexts | ||||
|     private final Collection<Model<Object>> allModels | ||||
|     private final Collection<Part> allParts | ||||
| 
 | ||||
|     PageToHtmlTask( | ||||
|             String relativeHtmlPath, | ||||
|             TaskSpec taskSpec, | ||||
|             Page page, | ||||
|             Collection<Text> allTexts, | ||||
|             Collection<Model<Object>> allModels, | ||||
|             Collection<Part> allParts | ||||
|     ) { | ||||
|         super( | ||||
|                 "pageToHtml:${ relativeHtmlPath }", | ||||
|                 relativeHtmlPath, | ||||
|                 new PageInput(page.path, page), | ||||
|                 taskSpec.outputDir | ||||
|         ) | ||||
|         this.siteSpec = taskSpec.siteSpec | ||||
|         this.globals = taskSpec.globals | ||||
|         this.page = page | ||||
|         this.allTexts = allTexts | ||||
|         this.allModels = allModels | ||||
|         this.allParts = allParts | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     protected Result<String> transform(Collection<Task> allTasks) { | ||||
|         this.page.type.renderer.render(this.page, new RenderContext( | ||||
|                 this.page.path, | ||||
|                 this.htmlPath, | ||||
|                 allTasks, | ||||
|                 this.allTexts, | ||||
|                 this.allModels, | ||||
|                 this.allParts, | ||||
|                 this.siteSpec, | ||||
|                 this.globals | ||||
|         )) | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     String toString() { | ||||
|         "PageToHtml(${ this.page }, ${ this.allTexts }, ${ this.allParts }, ${ super.toString() })" | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -1,39 +0,0 @@ | ||||
| package com.jessebrault.ssg.html | ||||
| 
 | ||||
| 
 | ||||
| import com.jessebrault.ssg.provider.CollectionProvider | ||||
| import com.jessebrault.ssg.provider.CollectionProviders | ||||
| import com.jessebrault.ssg.task.AbstractRenderTaskFactory | ||||
| import com.jessebrault.ssg.task.Task | ||||
| import com.jessebrault.ssg.task.TaskSpec | ||||
| import com.jessebrault.ssg.util.Result | ||||
| 
 | ||||
| import static java.util.Objects.requireNonNull | ||||
| 
 | ||||
| final class PageToHtmlTaskFactory extends AbstractRenderTaskFactory { | ||||
| 
 | ||||
|     CollectionProvider<PageToHtmlSpec> specsProvider = CollectionProviders.getEmpty() | ||||
| 
 | ||||
|     @Override | ||||
|     Result<Collection<Task>> getTasks(TaskSpec taskSpec) { | ||||
|         this.checkProviders() | ||||
|         requireNonNull(this.specsProvider) | ||||
| 
 | ||||
|         def allTexts = this.allTextsProvider.provide() | ||||
|         def allModels = this.allModelsProvider.provide() | ||||
|         def allParts = this.allPartsProvider.provide() | ||||
| 
 | ||||
|         final Collection<Task> tasks = this.specsProvider.provide() | ||||
|                 .collect { | ||||
|                     new PageToHtmlTask( | ||||
|                             it.toRelativeHtmlPath.apply(taskSpec), | ||||
|                             taskSpec, | ||||
|                             it.page, | ||||
|                             allTexts, | ||||
|                             allModels, | ||||
|                             allParts | ||||
|                     ) | ||||
|                 } | ||||
|         Result.of(tasks) | ||||
|     } | ||||
| } | ||||
| @ -1,15 +0,0 @@ | ||||
| package com.jessebrault.ssg.html | ||||
| 
 | ||||
| import groovy.transform.EqualsAndHashCode | ||||
| import groovy.transform.NullCheck | ||||
| import groovy.transform.PackageScope | ||||
| import groovy.transform.TupleConstructor | ||||
| 
 | ||||
| @TupleConstructor(defaults = false) | ||||
| @NullCheck(includeGenerated = true) | ||||
| @EqualsAndHashCode | ||||
| final class SimpleHtmlOutput implements HtmlOutput { | ||||
|     final String name | ||||
|     final File file | ||||
|     final String htmlPath | ||||
| } | ||||
| @ -1,19 +0,0 @@ | ||||
| package com.jessebrault.ssg.html | ||||
| 
 | ||||
| import com.jessebrault.ssg.task.TaskSpec | ||||
| import com.jessebrault.ssg.template.Template | ||||
| import com.jessebrault.ssg.text.Text | ||||
| import groovy.transform.EqualsAndHashCode | ||||
| import groovy.transform.NullCheck | ||||
| import groovy.transform.TupleConstructor | ||||
| 
 | ||||
| import java.util.function.Function | ||||
| 
 | ||||
| @TupleConstructor(defaults = false) | ||||
| @NullCheck(includeGenerated = true) | ||||
| @EqualsAndHashCode | ||||
| final class TextToHtmlSpec { | ||||
|     final Text text | ||||
|     final Template template | ||||
|     final Function<TaskSpec, String> toRelativeHtmlPath | ||||
| } | ||||
| @ -1,60 +0,0 @@ | ||||
| package com.jessebrault.ssg.html | ||||
| 
 | ||||
| import com.jessebrault.ssg.buildscript.SourceProviders | ||||
| import com.jessebrault.ssg.provider.CollectionProvider | ||||
| import com.jessebrault.ssg.provider.CollectionProviders | ||||
| import com.jessebrault.ssg.task.TaskSpec | ||||
| import com.jessebrault.ssg.template.Template | ||||
| import com.jessebrault.ssg.text.Text | ||||
| import com.jessebrault.ssg.util.ExtensionUtil | ||||
| import com.jessebrault.ssg.util.Result | ||||
| 
 | ||||
| import java.util.function.Function | ||||
| 
 | ||||
| final class TextToHtmlSpecProviders { | ||||
| 
 | ||||
|     static CollectionProvider<Result<TextToHtmlSpec>> from(SourceProviders sources) { | ||||
|         from(sources) { text -> | ||||
|             return { TaskSpec taskSpec -> | ||||
|                 ExtensionUtil.stripExtension(text.path) + '.html' | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     static CollectionProvider<Result<TextToHtmlSpec>> from( | ||||
|             SourceProviders sources, | ||||
|             Function<Text, Function<TaskSpec, String>> toRelativeHtmlPath | ||||
|     ) { | ||||
|         from(sources.textsProvider, sources.templatesProvider, toRelativeHtmlPath) | ||||
|     } | ||||
| 
 | ||||
|     static CollectionProvider<Result<TextToHtmlSpec>> from( | ||||
|             CollectionProvider<Text> textsProvider, | ||||
|             CollectionProvider<Template> templatesProvider, | ||||
|             Function<Text, Function<TaskSpec, String>> toRelativeHtmlPath | ||||
|     ) { | ||||
|         CollectionProviders.fromSupplier { | ||||
|             def templates = templatesProvider.provide() | ||||
|             textsProvider.provide().findResults { | ||||
|                 def frontMatterResult = it.type.frontMatterGetter.get(it) | ||||
|                 if (frontMatterResult.hasDiagnostics()) { | ||||
|                     return Result.ofDiagnostics(frontMatterResult.diagnostics) as Result<TextToHtmlSpec> | ||||
|                 } | ||||
|                 def templateValue = frontMatterResult.get().get('template') | ||||
|                 if (templateValue) { | ||||
|                     def template = templates.find { it.path == templateValue } | ||||
|                     return Result.of(new TextToHtmlSpec( | ||||
|                             it, | ||||
|                             template, | ||||
|                             toRelativeHtmlPath.apply(it) | ||||
|                     )) | ||||
|                 } else { | ||||
|                     return null | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private TextToHtmlSpecProviders() {} | ||||
| 
 | ||||
| } | ||||
| @ -1,71 +0,0 @@ | ||||
| package com.jessebrault.ssg.html | ||||
| 
 | ||||
| import com.jessebrault.ssg.SiteSpec | ||||
| import com.jessebrault.ssg.model.Model | ||||
| import com.jessebrault.ssg.part.Part | ||||
| import com.jessebrault.ssg.render.RenderContext | ||||
| import com.jessebrault.ssg.task.Task | ||||
| import com.jessebrault.ssg.task.TaskSpec | ||||
| import com.jessebrault.ssg.template.Template | ||||
| import com.jessebrault.ssg.text.Text | ||||
| import com.jessebrault.ssg.text.TextInput | ||||
| import com.jessebrault.ssg.util.Result | ||||
| import groovy.transform.EqualsAndHashCode | ||||
| import groovy.transform.NullCheck | ||||
| 
 | ||||
| @NullCheck | ||||
| @EqualsAndHashCode(includeFields = true, callSuper = true) | ||||
| final class TextToHtmlTask extends AbstractHtmlTask<TextInput> { | ||||
| 
 | ||||
|     private final SiteSpec siteSpec | ||||
|     private final Map<String, Object> globals | ||||
|     private final Text text | ||||
|     private final Template template | ||||
|     private final Collection<Text> allTexts | ||||
|     private final Collection<Model<Object>> allModels | ||||
|     private final Collection<Part> allParts | ||||
| 
 | ||||
|     TextToHtmlTask( | ||||
|             String relativeHtmlPath, | ||||
|             TaskSpec taskSpec, | ||||
|             Text text, | ||||
|             Template template, | ||||
|             Collection<Text> allTexts, | ||||
|             Collection<Model<Object>> allModels, | ||||
|             Collection<Part> allParts | ||||
|     ) { | ||||
|         super( | ||||
|                 "textToHtml:${ relativeHtmlPath }", | ||||
|                 relativeHtmlPath, | ||||
|                 new TextInput(text.path, text), | ||||
|                 taskSpec.outputDir | ||||
|         ) | ||||
|         this.siteSpec = taskSpec.siteSpec | ||||
|         this.globals = taskSpec.globals | ||||
|         this.text = text | ||||
|         this.template = template | ||||
|         this.allTexts = allTexts | ||||
|         this.allModels = allModels | ||||
|         this.allParts = allParts | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     protected Result<String> transform(Collection<Task> allTasks) { | ||||
|         this.template.type.renderer.render(this.template, this.text, new RenderContext( | ||||
|                 this.text.path, | ||||
|                 this.htmlPath, | ||||
|                 allTasks, | ||||
|                 this.allTexts, | ||||
|                 this.allModels, | ||||
|                 this.allParts, | ||||
|                 this.siteSpec, | ||||
|                 this.globals | ||||
|         )) | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     String toString() { | ||||
|         "TextToHtml(${ this.text }, ${ this.template }, ${ this.allTexts }, ${ this.allParts }, ${ super.toString() })" | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -1,47 +0,0 @@ | ||||
| package com.jessebrault.ssg.html | ||||
| 
 | ||||
| import com.jessebrault.ssg.provider.CollectionProvider | ||||
| import com.jessebrault.ssg.provider.CollectionProviders | ||||
| import com.jessebrault.ssg.task.AbstractRenderTaskFactory | ||||
| import com.jessebrault.ssg.task.Task | ||||
| import com.jessebrault.ssg.task.TaskSpec | ||||
| import com.jessebrault.ssg.util.Diagnostic | ||||
| import com.jessebrault.ssg.util.Result | ||||
| 
 | ||||
| import static java.util.Objects.requireNonNull | ||||
| 
 | ||||
| final class TextToHtmlTaskFactory extends AbstractRenderTaskFactory { | ||||
| 
 | ||||
|     CollectionProvider<Result<TextToHtmlSpec>> specsProvider = CollectionProviders.getEmpty() | ||||
| 
 | ||||
|     @Override | ||||
|     Result<Collection<Task>> getTasks(TaskSpec taskSpec) { | ||||
|         super.checkProviders() | ||||
|         requireNonNull(this.specsProvider) | ||||
| 
 | ||||
|         def allTexts = this.allTextsProvider.provide() | ||||
|         def allModels = this.allModelsProvider.provide() | ||||
|         def allParts = this.allPartsProvider.provide() | ||||
| 
 | ||||
|         Collection<Diagnostic> diagnostics = [] | ||||
| 
 | ||||
|         final Collection<Task> tasks = this.specsProvider.provide().findResults { | ||||
|             if (it.hasDiagnostics()) { | ||||
|                 diagnostics.addAll(it.diagnostics) | ||||
|             } else { | ||||
|                 def spec = it.get() | ||||
|                 new TextToHtmlTask( | ||||
|                         spec.toRelativeHtmlPath.apply(taskSpec), | ||||
|                         taskSpec, | ||||
|                         spec.text, | ||||
|                         spec.template, | ||||
|                         allTexts, | ||||
|                         allModels, | ||||
|                         allParts | ||||
|                 ) | ||||
|             } | ||||
|         } | ||||
|         Result.of(diagnostics, tasks) | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -2,5 +2,6 @@ package com.jessebrault.ssg.model | ||||
| 
 | ||||
| interface Model<T> { | ||||
|     String getName() | ||||
|     Class<T> getType() | ||||
|     T get() | ||||
| } | ||||
|  | ||||
| @ -1,14 +0,0 @@ | ||||
| package com.jessebrault.ssg.model | ||||
| 
 | ||||
| import com.jessebrault.ssg.task.TaskInput | ||||
| import groovy.transform.EqualsAndHashCode | ||||
| import groovy.transform.NullCheck | ||||
| import groovy.transform.TupleConstructor | ||||
| 
 | ||||
| @TupleConstructor(defaults = false) | ||||
| @NullCheck(includeGenerated = true) | ||||
| @EqualsAndHashCode | ||||
| final class ModelInput<T> implements TaskInput { | ||||
|     final String name | ||||
|     final Model<T> model | ||||
| } | ||||
| @ -7,20 +7,21 @@ import java.util.function.Supplier | ||||
| 
 | ||||
| final class Models { | ||||
| 
 | ||||
|     @SuppressWarnings('GroovyAssignabilityCheck') | ||||
|     static <T> Model<T> of(String name, T t) { | ||||
|         new SimpleModel<>(name, t) | ||||
|         new SimpleModel<>(name, t.class, t) | ||||
|     } | ||||
| 
 | ||||
|     static <T> Model<T> ofSupplier(String name, Supplier<? extends T> tClosure) { | ||||
|         new SupplierBasedModel<>(name, tClosure) | ||||
|     static <T> Model<T> ofSupplier(String name, Class<T> type, Supplier<? extends T> tClosure) { | ||||
|         new SupplierBasedModel<>(name, type, tClosure) | ||||
|     } | ||||
| 
 | ||||
|     static <T> Model<T> ofProvider(String name, Provider<? extends T> modelProvider) { | ||||
|         new ProviderModel<T>(name, modelProvider) | ||||
|         new ProviderModel<T>(name, modelProvider.type, modelProvider) | ||||
|     } | ||||
| 
 | ||||
|     static <T> Model<T> ofNamedProvider(NamedProvider<? extends T> namedModelProvider) { | ||||
|         new ProviderModel<T>(namedModelProvider.name, namedModelProvider) | ||||
|         new ProviderModel<T>(namedModelProvider.name, namedModelProvider.type, namedModelProvider) | ||||
|     } | ||||
| 
 | ||||
|     private Models() {} | ||||
|  | ||||
| @ -13,6 +13,7 @@ import groowt.util.fp.provider.Provider | ||||
| class ProviderModel<T> implements Model<T> { | ||||
| 
 | ||||
|     final String name | ||||
|     final Class<T> type | ||||
|     private final Provider<T> modelProvider | ||||
| 
 | ||||
|     @Override | ||||
| @ -22,6 +23,6 @@ class ProviderModel<T> implements Model<T> { | ||||
| 
 | ||||
|     @Override | ||||
|     String toString() { | ||||
|         "ProviderModel($this.name)" | ||||
|         "ProviderModel(name: $name, type: $type)" | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -12,6 +12,7 @@ import groovy.transform.TupleConstructor | ||||
| final class SimpleModel<T> implements Model<T> { | ||||
| 
 | ||||
|     final String name | ||||
|     final Class<T> type | ||||
|     private final T t | ||||
| 
 | ||||
|     @Override | ||||
| @ -21,7 +22,7 @@ final class SimpleModel<T> implements Model<T> { | ||||
| 
 | ||||
|     @Override | ||||
|     String toString() { | ||||
|         "SimpleModel(${ this.t })" | ||||
|         "SimpleModel(name: $name, type: $type.name)" | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  | ||||
| @ -8,21 +8,23 @@ import groovy.transform.TupleConstructor | ||||
| import java.util.function.Supplier | ||||
| 
 | ||||
| @PackageScope | ||||
| @TupleConstructor(includeFields = true, defaults = false) | ||||
| @NullCheck(includeGenerated = true) | ||||
| @EqualsAndHashCode(includeFields = true) | ||||
| final class SupplierBasedModel<T> implements Model<T> { | ||||
| 
 | ||||
|     final String name | ||||
|     private final Supplier<? extends T> supplier | ||||
|     final Class<T> type | ||||
| 
 | ||||
|     SupplierBasedModel(String name, Supplier<? extends T> supplier) { | ||||
|         this.name = name | ||||
|         this.supplier = supplier | ||||
|     } | ||||
|     private final Supplier<? extends T> supplier | ||||
| 
 | ||||
|     @Override | ||||
|     T get() { | ||||
|         this.supplier.get() | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     String toString() { | ||||
|         "SupplierBasedModel(name: $name, type: $type.name)" | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -0,0 +1,23 @@ | ||||
| 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<? extends PageView> viewType | ||||
| 
 | ||||
|     @Override | ||||
|     String toString() { | ||||
|         "SimplePage(name: $name, path: $path, fileExtension: $fileExtension)" | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -1,41 +0,0 @@ | ||||
| package com.jessebrault.ssg.page | ||||
| 
 | ||||
| import com.jessebrault.ssg.render.RenderContext | ||||
| import com.jessebrault.ssg.render.StandardGspRenderer | ||||
| import com.jessebrault.ssg.util.Result | ||||
| import groovy.transform.EqualsAndHashCode | ||||
| import groovy.transform.NullCheck | ||||
| 
 | ||||
| @NullCheck | ||||
| @EqualsAndHashCode | ||||
| final class GspPageRenderer implements PageRenderer { | ||||
| 
 | ||||
|     private final StandardGspRenderer gspRenderer | ||||
| 
 | ||||
|     GspPageRenderer(ClassLoader parentClassLoader) { | ||||
|         this.gspRenderer = new StandardGspRenderer(parentClassLoader) | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     Result<String> render( | ||||
|             Page specialPage, | ||||
|             RenderContext context | ||||
|     ) { | ||||
|         def diagnostics = [] | ||||
|         def result = this.gspRenderer.render(specialPage.text, context) { | ||||
|             it.diagnosticsConsumer = diagnostics.&addAll | ||||
|             it.loggerName = "GspSpecialPage(${ specialPage.path })" | ||||
|         } | ||||
|         if (result.hasDiagnostics()) { | ||||
|             Result.ofDiagnostics(diagnostics + result.diagnostics) | ||||
|         } else { | ||||
|             Result.of(diagnostics, result.get()) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     String toString() { | ||||
|         "GspSpecialPageRenderer()" | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -1,21 +1,10 @@ | ||||
| package com.jessebrault.ssg.page | ||||
| 
 | ||||
| import groovy.transform.EqualsAndHashCode | ||||
| import groovy.transform.NullCheck | ||||
| import groovy.transform.TupleConstructor | ||||
| 
 | ||||
| @TupleConstructor(defaults = false) | ||||
| @NullCheck(includeGenerated = true) | ||||
| @EqualsAndHashCode | ||||
| final class Page { | ||||
| 
 | ||||
|     final String path | ||||
|     final PageType type | ||||
|     final String text | ||||
| 
 | ||||
|     @Override | ||||
|     String toString() { | ||||
|         "Page(path: ${ this.path }, type: ${ this.type })" | ||||
|     } | ||||
| import com.jessebrault.ssg.view.PageView | ||||
| 
 | ||||
| interface Page { | ||||
|     String getName() | ||||
|     String getPath() | ||||
|     String getFileExtension() | ||||
|     Class<? extends PageView> getViewType() | ||||
| } | ||||
|  | ||||
| @ -0,0 +1,5 @@ | ||||
| package com.jessebrault.ssg.page | ||||
| 
 | ||||
| interface PageFactory { | ||||
|     Collection<Page> create() | ||||
| } | ||||
| @ -1,14 +0,0 @@ | ||||
| package com.jessebrault.ssg.page | ||||
| 
 | ||||
| import com.jessebrault.ssg.task.TaskInput | ||||
| import groovy.transform.EqualsAndHashCode | ||||
| import groovy.transform.NullCheck | ||||
| import groovy.transform.TupleConstructor | ||||
| 
 | ||||
| @TupleConstructor(defaults = false) | ||||
| @NullCheck(includeGenerated = true) | ||||
| @EqualsAndHashCode | ||||
| final class PageInput implements TaskInput { | ||||
|     final String name | ||||
|     final Page page | ||||
| } | ||||
| @ -1,13 +0,0 @@ | ||||
| package com.jessebrault.ssg.page | ||||
| 
 | ||||
| import com.jessebrault.ssg.render.RenderContext | ||||
| import com.jessebrault.ssg.util.Result | ||||
| 
 | ||||
| interface PageRenderer { | ||||
| 
 | ||||
|     Result<String> render( | ||||
|             Page specialPage, | ||||
|             RenderContext context | ||||
|     ) | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										14
									
								
								api/src/main/groovy/com/jessebrault/ssg/page/PageSpec.groovy
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								api/src/main/groovy/com/jessebrault/ssg/page/PageSpec.groovy
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,14 @@ | ||||
| package com.jessebrault.ssg.page | ||||
| 
 | ||||
| import java.lang.annotation.ElementType | ||||
| import java.lang.annotation.Retention | ||||
| import java.lang.annotation.RetentionPolicy | ||||
| import java.lang.annotation.Target | ||||
| 
 | ||||
| @Retention(RetentionPolicy.RUNTIME) | ||||
| @Target(ElementType.TYPE) | ||||
| @interface PageSpec { | ||||
|     String name() | ||||
|     String path() | ||||
|     String fileExtension() default '.html' | ||||
| } | ||||
| @ -1,20 +0,0 @@ | ||||
| package com.jessebrault.ssg.page | ||||
| 
 | ||||
| import groovy.transform.EqualsAndHashCode | ||||
| import groovy.transform.NullCheck | ||||
| import groovy.transform.TupleConstructor | ||||
| 
 | ||||
| @TupleConstructor(defaults = false) | ||||
| @NullCheck(includeGenerated = true) | ||||
| @EqualsAndHashCode | ||||
| final class PageType { | ||||
| 
 | ||||
|     Collection<String> ids | ||||
|     PageRenderer renderer | ||||
| 
 | ||||
|     @Override | ||||
|     String toString() { | ||||
|         "PageType(ids: ${ this.ids }, renderer: ${ this.renderer })" | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -1,14 +0,0 @@ | ||||
| package com.jessebrault.ssg.page | ||||
| 
 | ||||
| import groovy.transform.NullCheck | ||||
| 
 | ||||
| @NullCheck | ||||
| final class PageTypes { | ||||
| 
 | ||||
|     static PageType getGsp(Collection<String> extensions, ClassLoader parentClassLoader) { | ||||
|         new PageType(extensions, new GspPageRenderer(parentClassLoader)) | ||||
|     } | ||||
| 
 | ||||
|     private PageTypes() {} | ||||
| 
 | ||||
| } | ||||
| @ -1,50 +0,0 @@ | ||||
| package com.jessebrault.ssg.page | ||||
| 
 | ||||
| import com.jessebrault.ssg.provider.CollectionProvider | ||||
| import com.jessebrault.ssg.provider.CollectionProviders | ||||
| import com.jessebrault.ssg.util.ExtensionUtil | ||||
| import groovy.transform.NullCheck | ||||
| import org.slf4j.Logger | ||||
| import org.slf4j.LoggerFactory | ||||
| 
 | ||||
| import java.util.function.BiPredicate | ||||
| import java.util.function.Predicate | ||||
| 
 | ||||
| @NullCheck | ||||
| final class PagesProviders { | ||||
| 
 | ||||
|     private static final Logger logger = LoggerFactory.getLogger(PagesProviders) | ||||
| 
 | ||||
|     static CollectionProvider<Page> from(File pagesDirectory, Collection<PageType> pageTypes) { | ||||
|         from(pagesDirectory, pageTypes) { file, path -> true } | ||||
|     } | ||||
| 
 | ||||
|     static CollectionProvider<Page> from( | ||||
|             File pagesDirectory, | ||||
|             Collection<PageType> pageTypes, | ||||
|             BiPredicate<File, String> filter | ||||
|     ) { | ||||
|         CollectionProviders.fromDirectory(pagesDirectory) { file, relativePath -> | ||||
|             if (filter.test(file, relativePath)) { | ||||
|                 def extension = ExtensionUtil.getExtension(relativePath) | ||||
|                 if (extension) { | ||||
|                     def pageType = pageTypes.find { it.ids.contains(extension) } | ||||
|                     if (!pageType) { | ||||
|                         logger.debug('there is no PageType for file {}; skipping', file) | ||||
|                         return null | ||||
|                     } else { | ||||
|                         return new Page(relativePath, pageType, file.getText()) | ||||
|                     } | ||||
|                 } else { | ||||
|                     logger.debug('there is no extension for file {}; skipping', file) | ||||
|                     return null | ||||
|                 } | ||||
|             } else { | ||||
|                 return null | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private PagesProviders() {} | ||||
| 
 | ||||
| } | ||||
| @ -1,52 +0,0 @@ | ||||
| package com.jessebrault.ssg.part | ||||
| 
 | ||||
| import com.jessebrault.ssg.render.RenderContext | ||||
| import com.jessebrault.ssg.render.StandardGspRenderer | ||||
| import com.jessebrault.ssg.text.Text | ||||
| import com.jessebrault.ssg.util.Result | ||||
| import groovy.transform.EqualsAndHashCode | ||||
| import org.jetbrains.annotations.Nullable | ||||
| 
 | ||||
| import static java.util.Objects.requireNonNull | ||||
| 
 | ||||
| @EqualsAndHashCode | ||||
| final class GspPartRenderer implements PartRenderer { | ||||
| 
 | ||||
|     private final StandardGspRenderer gspRenderer | ||||
| 
 | ||||
|     GspPartRenderer(ClassLoader parentClassLoader) { | ||||
|         this.gspRenderer = new StandardGspRenderer(parentClassLoader) | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     Result<String> render( | ||||
|             Part part, | ||||
|             Map<String, Object> binding, | ||||
|             RenderContext context, | ||||
|             @Nullable Text text | ||||
|     ) { | ||||
|         requireNonNull(part) | ||||
|         requireNonNull(binding) | ||||
|         requireNonNull(context) | ||||
|         def diagnostics = [] | ||||
|         def result = this.gspRenderer.render(part.text, context) { | ||||
|             it.putCustom('binding', binding) | ||||
|             it.diagnosticsConsumer = diagnostics.&addAll | ||||
|             it.loggerName = "GspPart(${ part.path })" | ||||
|             if (text) { | ||||
|                 it.text = text | ||||
|             } | ||||
|         } | ||||
|         if (result.hasDiagnostics()) { | ||||
|             Result.ofDiagnostics(diagnostics + result.diagnostics) | ||||
|         } else { | ||||
|             Result.of(diagnostics, result.get()) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     String toString() { | ||||
|         "GspPartRenderer()" | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -1,21 +0,0 @@ | ||||
| package com.jessebrault.ssg.part | ||||
| 
 | ||||
| import groovy.transform.EqualsAndHashCode | ||||
| import groovy.transform.NullCheck | ||||
| import groovy.transform.TupleConstructor | ||||
| 
 | ||||
| @TupleConstructor(defaults = false) | ||||
| @NullCheck(includeGenerated = true) | ||||
| @EqualsAndHashCode | ||||
| final class Part { | ||||
| 
 | ||||
|     String path | ||||
|     PartType type | ||||
|     String text | ||||
| 
 | ||||
|     @Override | ||||
|     String toString() { | ||||
|         "Part(path: ${ this.path }, type: ${ this.type })" | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -1,17 +0,0 @@ | ||||
| package com.jessebrault.ssg.part | ||||
| 
 | ||||
| import com.jessebrault.ssg.render.RenderContext | ||||
| import com.jessebrault.ssg.util.Result | ||||
| import com.jessebrault.ssg.text.Text | ||||
| import org.jetbrains.annotations.Nullable | ||||
| 
 | ||||
| interface PartRenderer { | ||||
| 
 | ||||
|     Result<String> render( | ||||
|             Part part, | ||||
|             Map<String, Object> binding, | ||||
|             RenderContext context, | ||||
|             @Nullable Text text | ||||
|     ) | ||||
| 
 | ||||
| } | ||||
| @ -1,20 +0,0 @@ | ||||
| package com.jessebrault.ssg.part | ||||
| 
 | ||||
| import groovy.transform.EqualsAndHashCode | ||||
| import groovy.transform.NullCheck | ||||
| import groovy.transform.TupleConstructor | ||||
| 
 | ||||
| @TupleConstructor(defaults = false) | ||||
| @NullCheck(includeGenerated = true) | ||||
| @EqualsAndHashCode | ||||
| final class PartType { | ||||
| 
 | ||||
|     Collection<String> ids | ||||
|     PartRenderer renderer | ||||
| 
 | ||||
|     @Override | ||||
|     String toString() { | ||||
|         "PartType(ids: ${ this.ids }, renderer: ${ this.renderer })" | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -1,14 +0,0 @@ | ||||
| package com.jessebrault.ssg.part | ||||
| 
 | ||||
| import groovy.transform.NullCheck | ||||
| 
 | ||||
| @NullCheck | ||||
| final class PartTypes { | ||||
| 
 | ||||
|     static PartType getGsp(Collection<String> extensions, ClassLoader parentClassLoader) { | ||||
|         new PartType(extensions, new GspPartRenderer(parentClassLoader)) | ||||
|     } | ||||
| 
 | ||||
|     private PartTypes() {} | ||||
| 
 | ||||
| } | ||||
| @ -1,33 +0,0 @@ | ||||
| package com.jessebrault.ssg.part | ||||
| 
 | ||||
| import com.jessebrault.ssg.provider.CollectionProvider | ||||
| import com.jessebrault.ssg.provider.CollectionProviders | ||||
| import com.jessebrault.ssg.util.ExtensionUtil | ||||
| import org.slf4j.Logger | ||||
| import org.slf4j.LoggerFactory | ||||
| 
 | ||||
| final class PartsProviders { | ||||
| 
 | ||||
|     private static final Logger logger = LoggerFactory.getLogger(PartsProviders) | ||||
| 
 | ||||
|     static CollectionProvider<Part> from(File partsDir, Collection<PartType> partTypes) { | ||||
|         CollectionProviders.fromDirectory(partsDir) { file, relativePath -> | ||||
|             def extension = ExtensionUtil.getExtension(relativePath) | ||||
|             if (extension) { | ||||
|                 def partType = partTypes.find { it.ids.contains(extension) } | ||||
|                 if (!partType) { | ||||
|                     logger.debug('there is no PartType for file {}; skipping', file) | ||||
|                     return null | ||||
|                 } else { | ||||
|                     return new Part(relativePath, partType, file.getText()) | ||||
|                 } | ||||
|             } else { | ||||
|                 logger.debug('there is no extension for file {}; skipping', file) | ||||
|                 return null | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private PartsProviders() {} | ||||
| 
 | ||||
| } | ||||
| @ -1,73 +0,0 @@ | ||||
| package com.jessebrault.ssg.provider | ||||
| 
 | ||||
| import groovy.transform.EqualsAndHashCode | ||||
| import groovy.transform.NullCheck | ||||
| import groovy.transform.TupleConstructor | ||||
| 
 | ||||
| @TupleConstructor(defaults = false, includeFields = true) | ||||
| @NullCheck(includeGenerated = true) | ||||
| @EqualsAndHashCode(includeFields = true) | ||||
| abstract class AbstractCollectionProvider<T> implements CollectionProvider<T> { | ||||
| 
 | ||||
|     static <T> CollectionProvider<T> concat( | ||||
|             CollectionProvider<T> cp0, | ||||
|             CollectionProvider<T> cp1 | ||||
|     ) { | ||||
|         new SupplierBasedCollectionProvider<>([cp0, cp1], { | ||||
|             cp0.provide() + cp1.provide() | ||||
|         }) | ||||
|     } | ||||
| 
 | ||||
|     private final Collection<CollectionProvider<T>> collectionProviderChildren | ||||
| 
 | ||||
|     @Override | ||||
|     boolean contains(CollectionProvider<T> collectionProvider) { | ||||
|         collectionProvider in this | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     <C extends CollectionProvider<T>> boolean containsType(Class<C> childCollectionProviderClass) { | ||||
|         if (childCollectionProviderClass.isAssignableFrom(this.class)) { | ||||
|             true | ||||
|         } else { | ||||
|             this.collectionProviderChildren.inject(false) { acc, childProvider -> | ||||
|                 acc || childCollectionProviderClass.isAssignableFrom(childProvider.class) | ||||
|                         || childProvider.containsType(childCollectionProviderClass) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     <C extends CollectionProvider<T>> Collection<C> getChildrenOfType(Class<C> childCollectionProviderClass) { | ||||
|         this.collectionProviderChildren.inject([] as Collection<C>) { acc, childProvider -> | ||||
|             if (childCollectionProviderClass.isAssignableFrom(childProvider.class)) { | ||||
|                 acc + childProvider.getChildrenOfType(childCollectionProviderClass) + (childProvider as C) | ||||
|             } else { | ||||
|                 acc + childProvider.getChildrenOfType(childCollectionProviderClass) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     Collection<DirectoryCollectionProvider<T>> getDirectoryCollectionProviderChildren() { | ||||
|         this.getChildrenOfType(DirectoryCollectionProvider) | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     CollectionProvider<T> plus(CollectionProvider<T> other) { | ||||
|         concat(this, other) | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     boolean isCase(CollectionProvider<T> collectionProvider) { | ||||
|         collectionProvider == this | ||||
|                 || collectionProvider in this.collectionProviderChildren | ||||
|                 || this.collectionProviderChildren.inject( | ||||
|                         false, | ||||
|                         { acc, childCollectionProvider -> | ||||
|                             acc || collectionProvider in childCollectionProvider | ||||
|                         } | ||||
|                 ) | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -1,23 +0,0 @@ | ||||
| package com.jessebrault.ssg.provider; | ||||
| 
 | ||||
| import org.jetbrains.annotations.ApiStatus; | ||||
| 
 | ||||
| import java.util.Collection; | ||||
| 
 | ||||
| public interface CollectionProvider<T> { | ||||
|     Collection<T> provide(); | ||||
| 
 | ||||
|     boolean contains(CollectionProvider<T> collectionProvider); | ||||
| 
 | ||||
|     @ApiStatus.Experimental | ||||
|     <C extends CollectionProvider<T>> boolean containsType(Class<C> childCollectionProviderClass); | ||||
| 
 | ||||
|     @ApiStatus.Experimental | ||||
|     <C extends CollectionProvider<T>> Collection<C> getChildrenOfType(Class<C> childCollectionProviderClass); | ||||
| 
 | ||||
|     Collection<DirectoryCollectionProvider<T>> getDirectoryCollectionProviderChildren(); | ||||
| 
 | ||||
|     CollectionProvider<T> plus(CollectionProvider<T> other); | ||||
| 
 | ||||
|     boolean isCase(CollectionProvider<T> collectionProvider); | ||||
| } | ||||
| @ -1,31 +0,0 @@ | ||||
| package com.jessebrault.ssg.provider | ||||
| 
 | ||||
| import org.jetbrains.annotations.Nullable | ||||
| 
 | ||||
| import java.util.function.BiFunction | ||||
| import java.util.function.Supplier | ||||
| 
 | ||||
| final class CollectionProviders { | ||||
| 
 | ||||
|     static <T> CollectionProvider<T> getEmpty() { | ||||
|         new SimpleCollectionProvider<>([]) | ||||
|     } | ||||
| 
 | ||||
|     static <T> CollectionProvider<T> fromCollection(Collection<T> ts) { | ||||
|         new SimpleCollectionProvider<T>(ts) | ||||
|     } | ||||
| 
 | ||||
|     static <T> CollectionProvider<T> fromSupplier(Supplier<Collection<T>> supplier) { | ||||
|         new SupplierBasedCollectionProvider<>(supplier) | ||||
|     } | ||||
| 
 | ||||
|     static <T> DirectoryCollectionProvider<T> fromDirectory( | ||||
|             File baseDirectory, | ||||
|             BiFunction<File, String, @Nullable T> elementFunction | ||||
|     ) { | ||||
|         new FileBasedCollectionProvider<>(baseDirectory, elementFunction) | ||||
|     } | ||||
| 
 | ||||
|     private CollectionProviders() {} | ||||
| 
 | ||||
| } | ||||
| @ -1,11 +0,0 @@ | ||||
| package com.jessebrault.ssg.provider; | ||||
| 
 | ||||
| import org.jetbrains.annotations.Nullable; | ||||
| 
 | ||||
| import java.io.File; | ||||
| import java.util.function.BiFunction; | ||||
| 
 | ||||
| public interface DirectoryCollectionProvider<T> extends CollectionProvider<T> { | ||||
|     File getBaseDirectory(); | ||||
|     BiFunction<File, String, @Nullable T> getMapper(); | ||||
| } | ||||
| @ -1,57 +0,0 @@ | ||||
| package com.jessebrault.ssg.provider | ||||
| 
 | ||||
| import com.jessebrault.ssg.util.PathUtil | ||||
| import groovy.io.FileType | ||||
| import groovy.transform.EqualsAndHashCode | ||||
| import groovy.transform.NullCheck | ||||
| import groovy.transform.PackageScope | ||||
| import org.jetbrains.annotations.Nullable | ||||
| import org.slf4j.Logger | ||||
| import org.slf4j.LoggerFactory | ||||
| 
 | ||||
| import java.util.function.BiFunction | ||||
| 
 | ||||
| @PackageScope | ||||
| @NullCheck | ||||
| @EqualsAndHashCode(includeFields = true, callSuper = true) | ||||
| final class FileBasedCollectionProvider<T> extends AbstractCollectionProvider<T> implements DirectoryCollectionProvider<T> { | ||||
| 
 | ||||
|     private static final Logger logger = LoggerFactory.getLogger(FileBasedCollectionProvider) | ||||
| 
 | ||||
|     private final File baseDirectory | ||||
|     private final BiFunction<File, String, @Nullable T> elementFunction | ||||
| 
 | ||||
|     FileBasedCollectionProvider(File baseDirectory, BiFunction<File, String, T> elementFunction) { | ||||
|         super([], []) | ||||
|         this.baseDirectory = baseDirectory | ||||
|         this.elementFunction = elementFunction | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     File getBaseDirectory() { | ||||
|         this.baseDirectory | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     BiFunction<File, String, T> getMapper() { | ||||
|         this.elementFunction | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     Collection<T> provide() { | ||||
|         if (!this.baseDirectory.isDirectory()) { | ||||
|             logger.warn('{} does not exist or is not a directory; returning empty collection', this.baseDirectory) | ||||
|             [] | ||||
|         } else { | ||||
|             final Collection<T> ts = [] | ||||
|             this.baseDirectory.eachFileRecurse(FileType.FILES) { | ||||
|                 def t = this.elementFunction.apply(it, PathUtil.relative(this.baseDirectory.path, it.path)) as T | ||||
|                 if (t != null) { | ||||
|                     ts << t | ||||
|                 } | ||||
|             } | ||||
|             ts | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -1,27 +0,0 @@ | ||||
| package com.jessebrault.ssg.provider | ||||
| 
 | ||||
| import com.jessebrault.ssg.page.Page | ||||
| import groovy.transform.TupleConstructor | ||||
| import groowt.util.fp.provider.NamedProvider | ||||
| import groowt.util.fp.provider.Provider | ||||
| 
 | ||||
| @TupleConstructor(includeFields = true, force = true, defaults = false) | ||||
| class PageProvider implements NamedProvider<Page> { | ||||
| 
 | ||||
|     final String name | ||||
|     final String path | ||||
| 
 | ||||
|     private final Provider<Page> lazyPage | ||||
| 
 | ||||
|     PageProvider(String name, String path, Page page) { | ||||
|         this.name = name | ||||
|         this.path = path | ||||
|         this.lazyPage = { page } | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     Page get() { | ||||
|         this.lazyPage.get() | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -1,24 +0,0 @@ | ||||
| package com.jessebrault.ssg.provider | ||||
| 
 | ||||
| import groovy.transform.EqualsAndHashCode | ||||
| import groovy.transform.NullCheck | ||||
| import groovy.transform.PackageScope | ||||
| 
 | ||||
| @PackageScope | ||||
| @NullCheck | ||||
| @EqualsAndHashCode(includeFields = true, callSuper = true) | ||||
| final class SimpleCollectionProvider<T> extends AbstractCollectionProvider<T> { | ||||
| 
 | ||||
|     private final Collection<T> ts | ||||
| 
 | ||||
|     SimpleCollectionProvider(Collection<T> ts) { | ||||
|         super([], []) | ||||
|         this.ts = ts | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     Collection<T> provide() { | ||||
|         this.ts | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -1,33 +0,0 @@ | ||||
| package com.jessebrault.ssg.provider | ||||
| 
 | ||||
| import groovy.transform.EqualsAndHashCode | ||||
| import groovy.transform.NullCheck | ||||
| import groovy.transform.PackageScope | ||||
| 
 | ||||
| import java.util.function.Supplier | ||||
| 
 | ||||
| @PackageScope | ||||
| @NullCheck | ||||
| @EqualsAndHashCode(includeFields = true, callSuper = true) | ||||
| final class SupplierBasedCollectionProvider<T> extends AbstractCollectionProvider<T> { | ||||
| 
 | ||||
|     private final Supplier<Collection<T>> supplier | ||||
| 
 | ||||
|     SupplierBasedCollectionProvider( | ||||
|             Collection<CollectionProvider<T>> collectionProviderChildren, | ||||
|             Supplier<Collection<T>> supplier | ||||
|     ) { | ||||
|         super(collectionProviderChildren) | ||||
|         this.supplier = supplier | ||||
|     } | ||||
| 
 | ||||
|     SupplierBasedCollectionProvider(Supplier<Collection<T>> supplier) { | ||||
|         this([], supplier) | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     Collection<T> provide() { | ||||
|         this.supplier.get() | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -1,39 +0,0 @@ | ||||
| package com.jessebrault.ssg.render | ||||
| 
 | ||||
| import com.jessebrault.ssg.SiteSpec | ||||
| import com.jessebrault.ssg.model.Model | ||||
| import com.jessebrault.ssg.part.Part | ||||
| import com.jessebrault.ssg.task.Task | ||||
| import com.jessebrault.ssg.text.Text | ||||
| import groovy.transform.EqualsAndHashCode | ||||
| import groovy.transform.NullCheck | ||||
| import groovy.transform.TupleConstructor | ||||
| 
 | ||||
| @TupleConstructor(defaults = false, force = true) | ||||
| @NullCheck(includeGenerated = true) | ||||
| @EqualsAndHashCode | ||||
| final class RenderContext { | ||||
| 
 | ||||
|     RenderContext(Map<String, Object> args = [:]) { | ||||
|         this( | ||||
|                 args.sourcePath as String ?: '', | ||||
|                 args.targetPath as String ?: '', | ||||
|                 args.tasks as Collection<Task> ?: [], | ||||
|                 args.texts as Collection<Text> ?: [], | ||||
|                 args.models as Collection<Model<Object>> ?: [], | ||||
|                 args.parts as Collection<Part> ?: [], | ||||
|                 args.siteSpec as SiteSpec ?: SiteSpec.getBlank(), | ||||
|                 args.globals as Map<String, Object> ?: [:] | ||||
|         ) | ||||
|     } | ||||
| 
 | ||||
|     final String sourcePath | ||||
|     final String targetPath | ||||
|     final Collection<Task> tasks | ||||
|     final Collection<Text> texts | ||||
|     final Collection<Model<Object>> models | ||||
|     final Collection<Part> parts | ||||
|     final SiteSpec siteSpec | ||||
|     final Map<String, Object> globals | ||||
| 
 | ||||
| } | ||||
| @ -1,39 +0,0 @@ | ||||
| package com.jessebrault.ssg.render | ||||
| 
 | ||||
| import com.jessebrault.gst.TemplateCreator | ||||
| import com.jessebrault.gst.groovy.GroovyTemplateCreator | ||||
| import com.jessebrault.gst.parser.ExtendedGstParser | ||||
| import com.jessebrault.ssg.dsl.StandardDslMap | ||||
| import com.jessebrault.ssg.util.Diagnostic | ||||
| import com.jessebrault.ssg.util.Result | ||||
| 
 | ||||
| import java.util.function.Consumer | ||||
| 
 | ||||
| final class StandardGspRenderer { | ||||
| 
 | ||||
|     private final TemplateCreator templateCreator | ||||
| 
 | ||||
|     StandardGspRenderer(ClassLoader parentClassLoader) { | ||||
|         this.templateCreator = new GroovyTemplateCreator(ExtendedGstParser::new, parentClassLoader, true) | ||||
|     } | ||||
| 
 | ||||
|     Result<String> render( | ||||
|             String templateText, | ||||
|             RenderContext context, | ||||
|             Consumer<StandardDslMap.Builder> dslMapBuilderConsumer | ||||
|     ) { | ||||
|         try { | ||||
|             def templateCreateResult = this.templateCreator.create(templateText) | ||||
|             if (templateCreateResult.hasDiagnostics()) { | ||||
|                 Result.ofDiagnostics(templateCreateResult.diagnostics.collect { | ||||
|                     new Diagnostic(it.message, it.exception) | ||||
|                 }) | ||||
|             } else { | ||||
|                 Result.of(templateCreateResult.get().make(StandardDslMap.get(context, dslMapBuilderConsumer))) | ||||
|             } | ||||
|         } catch (Exception e) { | ||||
|             Result.ofDiagnostics([new Diagnostic(e.message, e)]) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -1,23 +0,0 @@ | ||||
| package com.jessebrault.ssg.task | ||||
| 
 | ||||
| import com.jessebrault.ssg.model.Model | ||||
| import com.jessebrault.ssg.part.Part | ||||
| import com.jessebrault.ssg.provider.CollectionProvider | ||||
| import com.jessebrault.ssg.provider.CollectionProviders | ||||
| import com.jessebrault.ssg.text.Text | ||||
| 
 | ||||
| import static java.util.Objects.requireNonNull | ||||
| 
 | ||||
| abstract class AbstractRenderTaskFactory implements TaskFactory { | ||||
| 
 | ||||
|     CollectionProvider<Text> allTextsProvider = CollectionProviders.getEmpty() | ||||
|     CollectionProvider<Model<Object>> allModelsProvider = CollectionProviders.getEmpty() | ||||
|     CollectionProvider<Part> allPartsProvider = CollectionProviders.getEmpty() | ||||
| 
 | ||||
|     protected final void checkProviders() { | ||||
|         requireNonNull(this.allTextsProvider) | ||||
|         requireNonNull(this.allModelsProvider) | ||||
|         requireNonNull(this.allPartsProvider) | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -1,19 +0,0 @@ | ||||
| package com.jessebrault.ssg.task | ||||
| 
 | ||||
| import groovy.transform.EqualsAndHashCode | ||||
| import groovy.transform.NullCheck | ||||
| import groovy.transform.TupleConstructor | ||||
| 
 | ||||
| @TupleConstructor(defaults = false) | ||||
| @NullCheck(includeGenerated = true) | ||||
| @EqualsAndHashCode | ||||
| abstract class AbstractTask implements Task { | ||||
| 
 | ||||
|     final String name | ||||
| 
 | ||||
|     @Override | ||||
|     String toString() { | ||||
|         "AbstractTask(name: ${ this.name })" | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -1,29 +0,0 @@ | ||||
| package com.jessebrault.ssg.task | ||||
| 
 | ||||
| import com.jessebrault.ssg.util.Result | ||||
| import groovy.transform.EqualsAndHashCode | ||||
| import groovy.transform.NullCheck | ||||
| import groovy.transform.PackageScope | ||||
| import groovy.transform.stc.ClosureParams | ||||
| import groovy.transform.stc.SimpleType | ||||
| 
 | ||||
| @PackageScope | ||||
| @NullCheck | ||||
| @EqualsAndHashCode(includeFields = true) | ||||
| final class ClosureBasedTaskFactory implements TaskFactory { | ||||
| 
 | ||||
|     private final Closure<Collection<Task>> closure | ||||
| 
 | ||||
|     ClosureBasedTaskFactory( | ||||
|             @ClosureParams(value = SimpleType, options = 'com.jessebrault.ssg.task.TaskSpec') | ||||
|             Closure<Collection<Task>> closure | ||||
|     ) { | ||||
|         this.closure = closure | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     Result<Collection<Task>> getTasks(TaskSpec taskSpec) { | ||||
|         this.closure(taskSpec) | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -1,5 +0,0 @@ | ||||
| package com.jessebrault.ssg.task | ||||
| 
 | ||||
| interface FileOutput extends TaskOutput { | ||||
|     File getFile() | ||||
| } | ||||
| @ -1,8 +0,0 @@ | ||||
| package com.jessebrault.ssg.task | ||||
| 
 | ||||
| import com.jessebrault.ssg.util.Diagnostic | ||||
| 
 | ||||
| interface Task { | ||||
|     String getName() | ||||
|     Collection<Diagnostic> execute(Collection<Task> allTasks) | ||||
| } | ||||
| @ -1,17 +0,0 @@ | ||||
| package com.jessebrault.ssg.task | ||||
| 
 | ||||
| import groovy.transform.stc.ClosureParams | ||||
| import groovy.transform.stc.SimpleType | ||||
| 
 | ||||
| final class TaskFactories { | ||||
| 
 | ||||
|     static TaskFactory of( | ||||
|             @ClosureParams(value = SimpleType, options = 'com.jessebrault.ssg.task.TaskSpec') | ||||
|             Closure<Collection<Void>> closure | ||||
|     ) { | ||||
|         new ClosureBasedTaskFactory(closure) | ||||
|     } | ||||
| 
 | ||||
|     private TaskFactories() {} | ||||
| 
 | ||||
| } | ||||
Some files were not shown because too many files have changed in this diff Show More
		Loading…
	
		Reference in New Issue
	
	Block a user
	 JesseBrault0709
						JesseBrault0709