From bc1d5452971553539083eab0280fd1c97f59c4ac Mon Sep 17 00:00:00 2001 From: JesseBrault0709 <62299747+JesseBrault0709@users.noreply.github.com> Date: Thu, 16 May 2024 10:34:46 +0200 Subject: [PATCH] Massive clean up and api/cli/gradle work. Running successfully now in test-ssg-project. --- .run/debug-test-project.run.xml | 16 + .run/launch-debug-test-project.run.xml | 19 ++ .run/run-test-project.run.xml | 19 ++ api/build.gradle | 23 +- ...BuildScriptBasedStaticSiteGenerator.groovy | 158 ---------- .../ssg/BuildTasksConverter.groovy | 9 - .../ssg/DefaultStaticSiteGenerator.groovy | 228 ++++++++++++++ .../ssg/SimpleBuildTasksConverter.groovy | 33 -- .../com/jessebrault/ssg/SiteSpec.groovy | 35 --- .../com/jessebrault/ssg/SsgException.groovy | 13 + .../ssg/StaticSiteGenerator.groovy | 13 +- .../com/jessebrault/ssg/build/Build.groovy | 38 --- .../ssg/buildscript/BuildScriptBase.groovy | 31 +- .../ssg/buildscript/BuildScriptGetter.groovy | 29 ++ ...=> BuildScriptToBuildSpecConverter.groovy} | 18 +- .../ssg/buildscript/BuildSpec.groovy | 23 +- .../buildscript/FileBuildScriptGetter.groovy | 19 -- .../delegates/BuildDelegate.groovy | 96 ++++-- .../com/jessebrault/ssg/di/Global.groovy | 15 + .../ssg/di/GlobalsExtension.groovy | 43 +++ .../com/jessebrault/ssg/di/InjectModel.groovy | 15 + .../ssg/di/InjectModelQualifierHandler.groovy | 26 ++ .../jessebrault/ssg/di/InjectModels.groovy | 15 + .../di/InjectModelsQualifierHandler.groovy | 27 ++ .../ssg/{objects => di}/InjectPage.groovy | 2 +- .../InjectPageQualifierHandler.groovy | 6 +- .../ssg/{objects => di}/InjectPages.groovy | 2 +- .../InjectPagesQualifierHandler.groovy | 12 +- .../ssg/{objects => di}/InjectText.groovy | 2 +- .../ssg/di/InjectTextQualifierHandler.groovy | 28 ++ .../ssg/{objects => di}/InjectTexts.groovy | 2 +- .../ssg/di/InjectTextsQualifierHandler.groovy | 39 +++ .../jessebrault/ssg/di/ModelsExtension.groovy | 28 ++ .../ssg/{objects => di}/PagesExtension.groovy | 7 +- .../com/jessebrault/ssg/di/SelfPage.groovy | 13 + .../ssg/di/SelfPageExtension.groovy | 42 +++ .../{objects => di}/SsgObjectFactory.groovy | 2 +- .../jessebrault/ssg/di/TextsExtension.groovy | 28 ++ .../jessebrault/ssg/dsl/EmbeddablePart.groovy | 56 ---- .../ssg/dsl/EmbeddablePartsMap.groovy | 36 --- .../jessebrault/ssg/dsl/EmbeddableText.groovy | 63 ---- .../ssg/dsl/EmbeddableTextsCollection.groovy | 28 -- .../ssg/dsl/ModelCollection.groovy | 84 ----- .../jessebrault/ssg/dsl/StandardDslMap.groovy | 81 ----- .../jessebrault/ssg/dsl/TaskCollection.groovy | 21 -- .../dsl/tagbuilder/DynamicTagBuilder.groovy | 77 ----- .../ssg/dsl/tagbuilder/TagBuilder.groovy | 8 - .../ssg/html/AbstractHtmlTask.groovy | 59 ---- .../jessebrault/ssg/html/HtmlOutput.groovy | 7 - .../com/jessebrault/ssg/html/HtmlTask.groovy | 12 - .../ssg/html/ModelToHtmlSpec.groovy | 16 - .../ssg/html/ModelToHtmlTask.groovy | 71 ----- .../ssg/html/ModelToHtmlTaskFactory.groovy | 33 -- .../ssg/html/PageToHtmlSpec.groovy | 17 - .../ssg/html/PageToHtmlSpecProviders.groovy | 34 -- .../ssg/html/PageToHtmlTask.groovy | 68 ---- .../ssg/html/PageToHtmlTaskFactory.groovy | 39 --- .../ssg/html/SimpleHtmlOutput.groovy | 15 - .../ssg/html/TextToHtmlSpec.groovy | 19 -- .../ssg/html/TextToHtmlSpecProviders.groovy | 60 ---- .../ssg/html/TextToHtmlTask.groovy | 71 ----- .../ssg/html/TextToHtmlTaskFactory.groovy | 47 --- .../com/jessebrault/ssg/model/Model.groovy | 1 + .../jessebrault/ssg/model/ModelInput.groovy | 14 - .../com/jessebrault/ssg/model/Models.groovy | 11 +- .../ssg/model/ProviderModel.groovy | 3 +- .../jessebrault/ssg/model/SimpleModel.groovy | 3 +- .../ssg/model/SupplierBasedModel.groovy | 12 +- .../jessebrault/ssg/page/DefaultPage.groovy | 23 ++ .../ssg/page/GspPageRenderer.groovy | 41 --- .../com/jessebrault/ssg/page/Page.groovy | 23 +- .../jessebrault/ssg/page/PageFactory.groovy | 5 + .../com/jessebrault/ssg/page/PageInput.groovy | 14 - .../jessebrault/ssg/page/PageRenderer.groovy | 13 - .../com/jessebrault/ssg/page/PageSpec.groovy | 14 + .../com/jessebrault/ssg/page/PageType.groovy | 20 -- .../com/jessebrault/ssg/page/PageTypes.groovy | 14 - .../ssg/page/PagesProviders.groovy | 50 --- .../ssg/part/GspPartRenderer.groovy | 52 ---- .../com/jessebrault/ssg/part/Part.groovy | 21 -- .../jessebrault/ssg/part/PartRenderer.groovy | 17 - .../com/jessebrault/ssg/part/PartType.groovy | 20 -- .../com/jessebrault/ssg/part/PartTypes.groovy | 14 - .../ssg/part/PartsProviders.groovy | 33 -- .../AbstractCollectionProvider.groovy | 73 ----- .../ssg/provider/CollectionProvider.java | 23 -- .../ssg/provider/CollectionProviders.groovy | 31 -- .../provider/DirectoryCollectionProvider.java | 11 - .../FileBasedCollectionProvider.groovy | 57 ---- .../ssg/provider/PageProvider.groovy | 27 -- .../provider/SimpleCollectionProvider.groovy | 24 -- .../SupplierBasedCollectionProvider.groovy | 33 -- .../ssg/render/RenderContext.groovy | 39 --- .../ssg/render/StandardGspRenderer.groovy | 39 --- .../ssg/task/AbstractRenderTaskFactory.groovy | 23 -- .../jessebrault/ssg/task/AbstractTask.groovy | 19 -- .../ssg/task/ClosureBasedTaskFactory.groovy | 29 -- .../jessebrault/ssg/task/FileOutput.groovy | 5 - .../com/jessebrault/ssg/task/Task.groovy | 8 - .../jessebrault/ssg/task/TaskFactories.groovy | 17 - .../jessebrault/ssg/task/TaskFactory.groovy | 7 - .../ssg/task/TaskFactorySpec.groovy | 53 ---- .../com/jessebrault/ssg/task/TaskInput.groovy | 5 - .../jessebrault/ssg/task/TaskOutput.groovy | 5 - .../com/jessebrault/ssg/task/TaskSpec.groovy | 22 -- .../ssg/template/GspTemplateRenderer.groovy | 44 --- .../jessebrault/ssg/template/Template.groovy | 21 -- .../ssg/template/TemplateRenderer.groovy | 15 - .../ssg/template/TemplateType.groovy | 20 -- .../ssg/template/TemplateTypes.groovy | 14 - .../ssg/template/TemplatesProviders.groovy | 33 -- .../jessebrault/ssg/text/ExcerptGetter.groovy | 7 - .../jessebrault/ssg/text/FrontMatter.groovy | 54 ---- .../ssg/text/FrontMatterGetter.groovy | 7 - .../ssg/text/MarkdownExcerptGetter.groovy | 55 ---- .../ssg/text/MarkdownFrontMatterGetter.groovy | 42 --- .../jessebrault/ssg/text/MarkdownText.groovy | 130 ++++++++ .../ssg/text/MarkdownTextConverter.groovy | 24 ++ .../ssg/text/MarkdownTextRenderer.groovy | 37 --- .../com/jessebrault/ssg/text/Text.groovy | 23 +- .../jessebrault/ssg/text/TextConverter.groovy | 6 + .../com/jessebrault/ssg/text/TextInput.groovy | 14 - .../jessebrault/ssg/text/TextRenderer.groovy | 7 - .../com/jessebrault/ssg/text/TextType.groovy | 22 -- .../com/jessebrault/ssg/text/TextTypes.groovy | 14 - .../com/jessebrault/ssg/text/TextUtil.java | 23 -- .../ssg/text/TextsProviders.groovy | 47 --- .../jessebrault/ssg/util/Diagnostic.groovy | 11 +- .../groovy/com/jessebrault/ssg/util/Eq.groovy | 7 - .../com/jessebrault/ssg/util/Eqs.groovy | 11 - .../com/jessebrault/ssg/util/Glob.groovy | 48 ++- .../com/jessebrault/ssg/util/Monoid.groovy | 6 - .../com/jessebrault/ssg/util/Monoids.groovy | 41 --- .../com/jessebrault/ssg/util/PathUtil.groovy | 4 + .../jessebrault/ssg/util/ResourceUtil.groovy | 27 -- .../com/jessebrault/ssg/util/Result.groovy | 45 --- .../com/jessebrault/ssg/util/Semigroup.groovy | 10 - .../jessebrault/ssg/util/Semigroups.groovy | 13 - .../com/jessebrault/ssg/util/SimpleEq.groovy | 16 - .../jessebrault/ssg/util/SimpleMonoid.groovy | 19 -- .../ssg/util/SimpleSemigroup.groovy | 18 -- .../com/jessebrault/ssg/util/URLUtil.groovy | 9 + .../com/jessebrault/ssg/util/Zero.groovy | 5 - .../com/jessebrault/ssg/view/PageView.groovy | 5 - .../ssg/view/WithHtmlHelpers.groovy | 14 + .../jessebrault/ssg/view/WvcPageView.groovy | 9 +- ...ScriptBasedStaticSiteGeneratorTests.groovy | 51 --- .../com/jessebrault/ssg/SiteSpecTests.groovy | 41 --- .../ssg/dsl/ModelCollectionTests.groovy | 26 -- .../tagbuilder/DynamicTagBuilderTests.groovy | 54 ---- .../ssg/page/GspPageRendererTests.groovy | 24 -- .../ssg/page/PagesProvidersTests.groovy | 58 ---- .../ssg/part/GspPartRendererTests.groovy | 119 ------- .../part/PartFilePartsProviderTests.groovy | 63 ---- .../template/GspTemplateRendererTests.groovy | 48 --- .../template/TemplatesProvidersTests.groovy | 61 ---- .../text/MarkdownExcerptGetterTests.groovy | 25 -- .../text/TextFileTextsProviderTests.groovy | 62 ---- .../ssg/util/ResourceUtilTests.groovy | 26 -- .../ssg/buildscript/TestHtmlTask.groovy | 34 -- .../buildscript/buildSrc/AnotherTask.groovy | 16 - .../ssg/buildscript/buildSrcTest.groovy | 4 - .../jessebrault/ssg/buildscript/simple.groovy | 15 - .../ssg/buildscript/testImport.groovy | 3 - .../ssg/buildscript/withBinding.groovy | 1 - api/src/test/resources/log4j2.xml | 17 - .../test/resources/oneTextAndTemplate.groovy | 42 --- api/src/test/resources/outputs/hello.html | 6 - .../testResourceDir/testResource.txt | 1 - .../ssg/dsl/DslScriptletProvider.groovy | 28 -- .../ssg/dsl/StandardDslConsumerTests.groovy | 290 ------------------ .../dsl/StandardDslConsumerTestsUtil.groovy | 33 -- .../com/jessebrault/ssg/page/PageMocks.groovy | 18 -- .../ssg/task/PageToHtmlTaskMocks.groovy | 13 - .../ssg/task/TextToHtmlTaskMocks.groovy | 14 - .../ssg/template/TemplateMocks.groovy | 12 - .../text/AbstractExcerptGetterTests.groovy | 39 --- .../com/jessebrault/ssg/text/TextMocks.groovy | 49 --- .../ssg/util/DiagnosticsUtil.groovy | 42 --- .../com/jessebrault/ssg/dsl/Greeter.groovy | 11 - .../com/jessebrault/ssg/dsl/TmpGreeter.groovy | 12 - .../testFixtures/resources/testResource.txt | 1 - cli/build.gradle | 13 + .../ssg/AbstractBuildCommand.groovy | 150 ++++++--- .../jessebrault/ssg/AbstractSubCommand.groovy | 13 +- .../ssg/CliBasedStaticSiteGenerator.groovy | 51 --- .../jessebrault/ssg/CommonCliOptions.groovy | 13 + .../com/jessebrault/ssg/LogLevel.groovy | 5 + .../com/jessebrault/ssg/SsgBuild.groovy | 11 +- .../groovy/com/jessebrault/ssg/SsgInit.groovy | 10 +- .../ssg/StaticSiteGeneratorCli.groovy | 25 +- cli/src/main/resources/log4j2.xml | 12 +- .../CliBasedStaticSiteGeneratorTests.groovy | 50 --- ...ticSiteGeneratorCliIntegrationTests.groovy | 6 - cli/src/test/resources/hello.html | 8 - cli/src/test/resources/log4j2.xml | 12 +- cli/src/test/resources/page.html | 8 - gradle/libs.versions.toml | 2 + settings.gradle | 2 +- ssg-gradle-model/build.gradle | 13 + .../ssg/gradle/DeafultSsgBuildModel.java | 23 ++ .../jessebrault/ssg/gradle/SsgBuildModel.java | 9 + ssg-gradle-plugin/build.gradle | 25 ++ .../ssg/gradle/SsgBinScriptTask.java | 82 +++++ .../ssg/gradle/SsgBuildModelBuilder.java | 33 ++ .../ssg/gradle/SsgGradlePlugin.java | 79 +++++ .../ssg/gradle/binScriptTemplate.gst | 8 + test-ssg-project/.gitignore | 1 + test-ssg-project/build.gradle | 8 + .../gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 43462 bytes .../gradle/wrapper/gradle-wrapper.properties | 7 + test-ssg-project/gradlew | 249 +++++++++++++++ test-ssg-project/gradlew.bat | 92 ++++++ test-ssg-project/settings.gradle | 8 + test-ssg-project/ssg/default.groovy | 4 + 215 files changed, 1914 insertions(+), 4618 deletions(-) create mode 100644 .run/debug-test-project.run.xml create mode 100644 .run/launch-debug-test-project.run.xml create mode 100644 .run/run-test-project.run.xml delete mode 100644 api/src/main/groovy/com/jessebrault/ssg/BuildScriptBasedStaticSiteGenerator.groovy delete mode 100644 api/src/main/groovy/com/jessebrault/ssg/BuildTasksConverter.groovy create mode 100644 api/src/main/groovy/com/jessebrault/ssg/DefaultStaticSiteGenerator.groovy delete mode 100644 api/src/main/groovy/com/jessebrault/ssg/SimpleBuildTasksConverter.groovy delete mode 100644 api/src/main/groovy/com/jessebrault/ssg/SiteSpec.groovy create mode 100644 api/src/main/groovy/com/jessebrault/ssg/SsgException.groovy delete mode 100644 api/src/main/groovy/com/jessebrault/ssg/build/Build.groovy create mode 100644 api/src/main/groovy/com/jessebrault/ssg/buildscript/BuildScriptGetter.groovy rename api/src/main/groovy/com/jessebrault/ssg/buildscript/{BuildDelegateToBuildSpecConverter.groovy => BuildScriptToBuildSpecConverter.groovy} (67%) delete mode 100644 api/src/main/groovy/com/jessebrault/ssg/buildscript/FileBuildScriptGetter.groovy create mode 100644 api/src/main/groovy/com/jessebrault/ssg/di/Global.groovy create mode 100644 api/src/main/groovy/com/jessebrault/ssg/di/GlobalsExtension.groovy create mode 100644 api/src/main/groovy/com/jessebrault/ssg/di/InjectModel.groovy create mode 100644 api/src/main/groovy/com/jessebrault/ssg/di/InjectModelQualifierHandler.groovy create mode 100644 api/src/main/groovy/com/jessebrault/ssg/di/InjectModels.groovy create mode 100644 api/src/main/groovy/com/jessebrault/ssg/di/InjectModelsQualifierHandler.groovy rename api/src/main/groovy/com/jessebrault/ssg/{objects => di}/InjectPage.groovy (92%) rename api/src/main/groovy/com/jessebrault/ssg/{objects => di}/InjectPageQualifierHandler.groovy (87%) rename api/src/main/groovy/com/jessebrault/ssg/{objects => di}/InjectPages.groovy (92%) rename api/src/main/groovy/com/jessebrault/ssg/{objects => di}/InjectPagesQualifierHandler.groovy (76%) rename api/src/main/groovy/com/jessebrault/ssg/{objects => di}/InjectText.groovy (92%) create mode 100644 api/src/main/groovy/com/jessebrault/ssg/di/InjectTextQualifierHandler.groovy rename api/src/main/groovy/com/jessebrault/ssg/{objects => di}/InjectTexts.groovy (92%) create mode 100644 api/src/main/groovy/com/jessebrault/ssg/di/InjectTextsQualifierHandler.groovy create mode 100644 api/src/main/groovy/com/jessebrault/ssg/di/ModelsExtension.groovy rename api/src/main/groovy/com/jessebrault/ssg/{objects => di}/PagesExtension.groovy (85%) create mode 100644 api/src/main/groovy/com/jessebrault/ssg/di/SelfPage.groovy create mode 100644 api/src/main/groovy/com/jessebrault/ssg/di/SelfPageExtension.groovy rename api/src/main/groovy/com/jessebrault/ssg/{objects => di}/SsgObjectFactory.groovy (92%) create mode 100644 api/src/main/groovy/com/jessebrault/ssg/di/TextsExtension.groovy delete mode 100644 api/src/main/groovy/com/jessebrault/ssg/dsl/EmbeddablePart.groovy delete mode 100644 api/src/main/groovy/com/jessebrault/ssg/dsl/EmbeddablePartsMap.groovy delete mode 100644 api/src/main/groovy/com/jessebrault/ssg/dsl/EmbeddableText.groovy delete mode 100644 api/src/main/groovy/com/jessebrault/ssg/dsl/EmbeddableTextsCollection.groovy delete mode 100644 api/src/main/groovy/com/jessebrault/ssg/dsl/ModelCollection.groovy delete mode 100644 api/src/main/groovy/com/jessebrault/ssg/dsl/StandardDslMap.groovy delete mode 100644 api/src/main/groovy/com/jessebrault/ssg/dsl/TaskCollection.groovy delete mode 100644 api/src/main/groovy/com/jessebrault/ssg/dsl/tagbuilder/DynamicTagBuilder.groovy delete mode 100644 api/src/main/groovy/com/jessebrault/ssg/dsl/tagbuilder/TagBuilder.groovy delete mode 100644 api/src/main/groovy/com/jessebrault/ssg/html/AbstractHtmlTask.groovy delete mode 100644 api/src/main/groovy/com/jessebrault/ssg/html/HtmlOutput.groovy delete mode 100644 api/src/main/groovy/com/jessebrault/ssg/html/HtmlTask.groovy delete mode 100644 api/src/main/groovy/com/jessebrault/ssg/html/ModelToHtmlSpec.groovy delete mode 100644 api/src/main/groovy/com/jessebrault/ssg/html/ModelToHtmlTask.groovy delete mode 100644 api/src/main/groovy/com/jessebrault/ssg/html/ModelToHtmlTaskFactory.groovy delete mode 100644 api/src/main/groovy/com/jessebrault/ssg/html/PageToHtmlSpec.groovy delete mode 100644 api/src/main/groovy/com/jessebrault/ssg/html/PageToHtmlSpecProviders.groovy delete mode 100644 api/src/main/groovy/com/jessebrault/ssg/html/PageToHtmlTask.groovy delete mode 100644 api/src/main/groovy/com/jessebrault/ssg/html/PageToHtmlTaskFactory.groovy delete mode 100644 api/src/main/groovy/com/jessebrault/ssg/html/SimpleHtmlOutput.groovy delete mode 100644 api/src/main/groovy/com/jessebrault/ssg/html/TextToHtmlSpec.groovy delete mode 100644 api/src/main/groovy/com/jessebrault/ssg/html/TextToHtmlSpecProviders.groovy delete mode 100644 api/src/main/groovy/com/jessebrault/ssg/html/TextToHtmlTask.groovy delete mode 100644 api/src/main/groovy/com/jessebrault/ssg/html/TextToHtmlTaskFactory.groovy delete mode 100644 api/src/main/groovy/com/jessebrault/ssg/model/ModelInput.groovy create mode 100644 api/src/main/groovy/com/jessebrault/ssg/page/DefaultPage.groovy delete mode 100644 api/src/main/groovy/com/jessebrault/ssg/page/GspPageRenderer.groovy create mode 100644 api/src/main/groovy/com/jessebrault/ssg/page/PageFactory.groovy delete mode 100644 api/src/main/groovy/com/jessebrault/ssg/page/PageInput.groovy delete mode 100644 api/src/main/groovy/com/jessebrault/ssg/page/PageRenderer.groovy create mode 100644 api/src/main/groovy/com/jessebrault/ssg/page/PageSpec.groovy delete mode 100644 api/src/main/groovy/com/jessebrault/ssg/page/PageType.groovy delete mode 100644 api/src/main/groovy/com/jessebrault/ssg/page/PageTypes.groovy delete mode 100644 api/src/main/groovy/com/jessebrault/ssg/page/PagesProviders.groovy delete mode 100644 api/src/main/groovy/com/jessebrault/ssg/part/GspPartRenderer.groovy delete mode 100644 api/src/main/groovy/com/jessebrault/ssg/part/Part.groovy delete mode 100644 api/src/main/groovy/com/jessebrault/ssg/part/PartRenderer.groovy delete mode 100644 api/src/main/groovy/com/jessebrault/ssg/part/PartType.groovy delete mode 100644 api/src/main/groovy/com/jessebrault/ssg/part/PartTypes.groovy delete mode 100644 api/src/main/groovy/com/jessebrault/ssg/part/PartsProviders.groovy delete mode 100644 api/src/main/groovy/com/jessebrault/ssg/provider/AbstractCollectionProvider.groovy delete mode 100644 api/src/main/groovy/com/jessebrault/ssg/provider/CollectionProvider.java delete mode 100644 api/src/main/groovy/com/jessebrault/ssg/provider/CollectionProviders.groovy delete mode 100644 api/src/main/groovy/com/jessebrault/ssg/provider/DirectoryCollectionProvider.java delete mode 100644 api/src/main/groovy/com/jessebrault/ssg/provider/FileBasedCollectionProvider.groovy delete mode 100644 api/src/main/groovy/com/jessebrault/ssg/provider/PageProvider.groovy delete mode 100644 api/src/main/groovy/com/jessebrault/ssg/provider/SimpleCollectionProvider.groovy delete mode 100644 api/src/main/groovy/com/jessebrault/ssg/provider/SupplierBasedCollectionProvider.groovy delete mode 100644 api/src/main/groovy/com/jessebrault/ssg/render/RenderContext.groovy delete mode 100644 api/src/main/groovy/com/jessebrault/ssg/render/StandardGspRenderer.groovy delete mode 100644 api/src/main/groovy/com/jessebrault/ssg/task/AbstractRenderTaskFactory.groovy delete mode 100644 api/src/main/groovy/com/jessebrault/ssg/task/AbstractTask.groovy delete mode 100644 api/src/main/groovy/com/jessebrault/ssg/task/ClosureBasedTaskFactory.groovy delete mode 100644 api/src/main/groovy/com/jessebrault/ssg/task/FileOutput.groovy delete mode 100644 api/src/main/groovy/com/jessebrault/ssg/task/Task.groovy delete mode 100644 api/src/main/groovy/com/jessebrault/ssg/task/TaskFactories.groovy delete mode 100644 api/src/main/groovy/com/jessebrault/ssg/task/TaskFactory.groovy delete mode 100644 api/src/main/groovy/com/jessebrault/ssg/task/TaskFactorySpec.groovy delete mode 100644 api/src/main/groovy/com/jessebrault/ssg/task/TaskInput.groovy delete mode 100644 api/src/main/groovy/com/jessebrault/ssg/task/TaskOutput.groovy delete mode 100644 api/src/main/groovy/com/jessebrault/ssg/task/TaskSpec.groovy delete mode 100644 api/src/main/groovy/com/jessebrault/ssg/template/GspTemplateRenderer.groovy delete mode 100644 api/src/main/groovy/com/jessebrault/ssg/template/Template.groovy delete mode 100644 api/src/main/groovy/com/jessebrault/ssg/template/TemplateRenderer.groovy delete mode 100644 api/src/main/groovy/com/jessebrault/ssg/template/TemplateType.groovy delete mode 100644 api/src/main/groovy/com/jessebrault/ssg/template/TemplateTypes.groovy delete mode 100644 api/src/main/groovy/com/jessebrault/ssg/template/TemplatesProviders.groovy delete mode 100644 api/src/main/groovy/com/jessebrault/ssg/text/ExcerptGetter.groovy delete mode 100644 api/src/main/groovy/com/jessebrault/ssg/text/FrontMatter.groovy delete mode 100644 api/src/main/groovy/com/jessebrault/ssg/text/FrontMatterGetter.groovy delete mode 100644 api/src/main/groovy/com/jessebrault/ssg/text/MarkdownExcerptGetter.groovy delete mode 100644 api/src/main/groovy/com/jessebrault/ssg/text/MarkdownFrontMatterGetter.groovy create mode 100644 api/src/main/groovy/com/jessebrault/ssg/text/MarkdownText.groovy create mode 100644 api/src/main/groovy/com/jessebrault/ssg/text/MarkdownTextConverter.groovy delete mode 100644 api/src/main/groovy/com/jessebrault/ssg/text/MarkdownTextRenderer.groovy create mode 100644 api/src/main/groovy/com/jessebrault/ssg/text/TextConverter.groovy delete mode 100644 api/src/main/groovy/com/jessebrault/ssg/text/TextInput.groovy delete mode 100644 api/src/main/groovy/com/jessebrault/ssg/text/TextRenderer.groovy delete mode 100644 api/src/main/groovy/com/jessebrault/ssg/text/TextType.groovy delete mode 100644 api/src/main/groovy/com/jessebrault/ssg/text/TextTypes.groovy delete mode 100644 api/src/main/groovy/com/jessebrault/ssg/text/TextUtil.java delete mode 100644 api/src/main/groovy/com/jessebrault/ssg/text/TextsProviders.groovy delete mode 100644 api/src/main/groovy/com/jessebrault/ssg/util/Eq.groovy delete mode 100644 api/src/main/groovy/com/jessebrault/ssg/util/Eqs.groovy delete mode 100644 api/src/main/groovy/com/jessebrault/ssg/util/Monoid.groovy delete mode 100644 api/src/main/groovy/com/jessebrault/ssg/util/Monoids.groovy delete mode 100644 api/src/main/groovy/com/jessebrault/ssg/util/ResourceUtil.groovy delete mode 100644 api/src/main/groovy/com/jessebrault/ssg/util/Result.groovy delete mode 100644 api/src/main/groovy/com/jessebrault/ssg/util/Semigroup.groovy delete mode 100644 api/src/main/groovy/com/jessebrault/ssg/util/Semigroups.groovy delete mode 100644 api/src/main/groovy/com/jessebrault/ssg/util/SimpleEq.groovy delete mode 100644 api/src/main/groovy/com/jessebrault/ssg/util/SimpleMonoid.groovy delete mode 100644 api/src/main/groovy/com/jessebrault/ssg/util/SimpleSemigroup.groovy create mode 100644 api/src/main/groovy/com/jessebrault/ssg/util/URLUtil.groovy delete mode 100644 api/src/main/groovy/com/jessebrault/ssg/util/Zero.groovy create mode 100644 api/src/main/groovy/com/jessebrault/ssg/view/WithHtmlHelpers.groovy delete mode 100644 api/src/test/groovy/com/jessebrault/ssg/BuildScriptBasedStaticSiteGeneratorTests.groovy delete mode 100644 api/src/test/groovy/com/jessebrault/ssg/SiteSpecTests.groovy delete mode 100644 api/src/test/groovy/com/jessebrault/ssg/dsl/ModelCollectionTests.groovy delete mode 100644 api/src/test/groovy/com/jessebrault/ssg/dsl/tagbuilder/DynamicTagBuilderTests.groovy delete mode 100644 api/src/test/groovy/com/jessebrault/ssg/page/GspPageRendererTests.groovy delete mode 100644 api/src/test/groovy/com/jessebrault/ssg/page/PagesProvidersTests.groovy delete mode 100644 api/src/test/groovy/com/jessebrault/ssg/part/GspPartRendererTests.groovy delete mode 100644 api/src/test/groovy/com/jessebrault/ssg/part/PartFilePartsProviderTests.groovy delete mode 100644 api/src/test/groovy/com/jessebrault/ssg/template/GspTemplateRendererTests.groovy delete mode 100644 api/src/test/groovy/com/jessebrault/ssg/template/TemplatesProvidersTests.groovy delete mode 100644 api/src/test/groovy/com/jessebrault/ssg/text/MarkdownExcerptGetterTests.groovy delete mode 100644 api/src/test/groovy/com/jessebrault/ssg/text/TextFileTextsProviderTests.groovy delete mode 100644 api/src/test/groovy/com/jessebrault/ssg/util/ResourceUtilTests.groovy delete mode 100644 api/src/test/resources/com/jessebrault/ssg/buildscript/TestHtmlTask.groovy delete mode 100644 api/src/test/resources/com/jessebrault/ssg/buildscript/buildSrc/AnotherTask.groovy delete mode 100644 api/src/test/resources/com/jessebrault/ssg/buildscript/buildSrcTest.groovy delete mode 100644 api/src/test/resources/com/jessebrault/ssg/buildscript/simple.groovy delete mode 100644 api/src/test/resources/com/jessebrault/ssg/buildscript/testImport.groovy delete mode 100644 api/src/test/resources/com/jessebrault/ssg/buildscript/withBinding.groovy delete mode 100644 api/src/test/resources/log4j2.xml delete mode 100644 api/src/test/resources/oneTextAndTemplate.groovy delete mode 100644 api/src/test/resources/outputs/hello.html delete mode 100644 api/src/test/resources/testResourceDir/testResource.txt delete mode 100644 api/src/testFixtures/groovy/com/jessebrault/ssg/dsl/DslScriptletProvider.groovy delete mode 100644 api/src/testFixtures/groovy/com/jessebrault/ssg/dsl/StandardDslConsumerTests.groovy delete mode 100644 api/src/testFixtures/groovy/com/jessebrault/ssg/dsl/StandardDslConsumerTestsUtil.groovy delete mode 100644 api/src/testFixtures/groovy/com/jessebrault/ssg/page/PageMocks.groovy delete mode 100644 api/src/testFixtures/groovy/com/jessebrault/ssg/task/PageToHtmlTaskMocks.groovy delete mode 100644 api/src/testFixtures/groovy/com/jessebrault/ssg/task/TextToHtmlTaskMocks.groovy delete mode 100644 api/src/testFixtures/groovy/com/jessebrault/ssg/template/TemplateMocks.groovy delete mode 100644 api/src/testFixtures/groovy/com/jessebrault/ssg/text/AbstractExcerptGetterTests.groovy delete mode 100644 api/src/testFixtures/groovy/com/jessebrault/ssg/text/TextMocks.groovy delete mode 100644 api/src/testFixtures/groovy/com/jessebrault/ssg/util/DiagnosticsUtil.groovy delete mode 100644 api/src/testFixtures/resources/com/jessebrault/ssg/dsl/Greeter.groovy delete mode 100644 api/src/testFixtures/resources/com/jessebrault/ssg/dsl/TmpGreeter.groovy delete mode 100644 api/src/testFixtures/resources/testResource.txt delete mode 100644 cli/src/main/groovy/com/jessebrault/ssg/CliBasedStaticSiteGenerator.groovy create mode 100644 cli/src/main/groovy/com/jessebrault/ssg/CommonCliOptions.groovy create mode 100644 cli/src/main/groovy/com/jessebrault/ssg/LogLevel.groovy delete mode 100644 cli/src/test/groovy/com/jessebrault/ssg/CliBasedStaticSiteGeneratorTests.groovy delete mode 100644 cli/src/test/groovy/com/jessebrault/ssg/StaticSiteGeneratorCliIntegrationTests.groovy delete mode 100644 cli/src/test/resources/hello.html delete mode 100644 cli/src/test/resources/page.html create mode 100644 ssg-gradle-model/build.gradle create mode 100644 ssg-gradle-model/src/main/java/com/jessebrault/ssg/gradle/DeafultSsgBuildModel.java create mode 100644 ssg-gradle-model/src/main/java/com/jessebrault/ssg/gradle/SsgBuildModel.java create mode 100644 ssg-gradle-plugin/src/main/java/com/jessebrault/ssg/gradle/SsgBinScriptTask.java create mode 100644 ssg-gradle-plugin/src/main/java/com/jessebrault/ssg/gradle/SsgBuildModelBuilder.java create mode 100644 ssg-gradle-plugin/src/main/java/com/jessebrault/ssg/gradle/SsgGradlePlugin.java create mode 100644 ssg-gradle-plugin/src/main/resources/com/jessebrault/ssg/gradle/binScriptTemplate.gst create mode 100644 test-ssg-project/.gitignore create mode 100644 test-ssg-project/build.gradle create mode 100644 test-ssg-project/gradle/wrapper/gradle-wrapper.jar create mode 100644 test-ssg-project/gradle/wrapper/gradle-wrapper.properties create mode 100755 test-ssg-project/gradlew create mode 100644 test-ssg-project/gradlew.bat create mode 100644 test-ssg-project/settings.gradle create mode 100644 test-ssg-project/ssg/default.groovy diff --git a/.run/debug-test-project.run.xml b/.run/debug-test-project.run.xml new file mode 100644 index 0000000..b7ef759 --- /dev/null +++ b/.run/debug-test-project.run.xml @@ -0,0 +1,16 @@ + + + + + \ No newline at end of file diff --git a/.run/launch-debug-test-project.run.xml b/.run/launch-debug-test-project.run.xml new file mode 100644 index 0000000..27124ba --- /dev/null +++ b/.run/launch-debug-test-project.run.xml @@ -0,0 +1,19 @@ + + + + \ No newline at end of file diff --git a/.run/run-test-project.run.xml b/.run/run-test-project.run.xml new file mode 100644 index 0000000..890c4e0 --- /dev/null +++ b/.run/run-test-project.run.xml @@ -0,0 +1,19 @@ + + + + \ No newline at end of file diff --git a/api/build.gradle b/api/build.gradle index 8a11427..ea99501 100644 --- a/api/build.gradle +++ b/api/build.gradle @@ -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 + } + } +} diff --git a/api/src/main/groovy/com/jessebrault/ssg/BuildScriptBasedStaticSiteGenerator.groovy b/api/src/main/groovy/com/jessebrault/ssg/BuildScriptBasedStaticSiteGenerator.groovy deleted file mode 100644 index 2282efd..0000000 --- a/api/src/main/groovy/com/jessebrault/ssg/BuildScriptBasedStaticSiteGenerator.groovy +++ /dev/null @@ -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 buildScriptClassLoaderUrls - private final @Nullable File buildScript - private final Collection 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 null. - */ - BuildScriptBasedStaticSiteGenerator( - Collection buildScriptClassLoaderUrls, - @Nullable File buildScript = null - ) { - this.buildScriptClassLoaderUrls = buildScriptClassLoaderUrls - this.buildScript = buildScript - } - - private void runBuildScript( - Collection configuratorFactories, - Map 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 builds - final Collection diagnostics - } - - // TODO: cache build script results - @Override - boolean doBuild( - String buildName, - Collection configuratorFactories, - Map buildScriptArgs, - Consumer> 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 - } - -} diff --git a/api/src/main/groovy/com/jessebrault/ssg/BuildTasksConverter.groovy b/api/src/main/groovy/com/jessebrault/ssg/BuildTasksConverter.groovy deleted file mode 100644 index fbbff9c..0000000 --- a/api/src/main/groovy/com/jessebrault/ssg/BuildTasksConverter.groovy +++ /dev/null @@ -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> convert(BuildSpec buildScriptResult) -} diff --git a/api/src/main/groovy/com/jessebrault/ssg/DefaultStaticSiteGenerator.groovy b/api/src/main/groovy/com/jessebrault/ssg/DefaultStaticSiteGenerator.groovy new file mode 100644 index 0000000..337a6be --- /dev/null +++ b/api/src/main/groovy/com/jessebrault/ssg/DefaultStaticSiteGenerator.groovy @@ -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 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 + 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 doBuild( + File projectDir, + String buildName, + String buildScriptFqn, + Map 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 + + 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) pageViewInfo.loadClass() + ) + } + } + + // page factories + def pageFactoryTypes = [] as Set> + + def pageFactoryInfoList = scanResult.getClassesImplementing(PageFactory) + pageFactoryInfoList.each { pageFactoryInfo -> + pageFactoryTypes << (pageFactoryInfo.loadClass() as Class) + } + + // 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 + + 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 + } + +} diff --git a/api/src/main/groovy/com/jessebrault/ssg/SimpleBuildTasksConverter.groovy b/api/src/main/groovy/com/jessebrault/ssg/SimpleBuildTasksConverter.groovy deleted file mode 100644 index aa2308e..0000000 --- a/api/src/main/groovy/com/jessebrault/ssg/SimpleBuildTasksConverter.groovy +++ /dev/null @@ -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> convert(BuildSpec build) { - def taskSpec = new TaskSpec( - build.name, - build.outputDirFunction.apply(build).asFile(), - build.siteSpec, - build.globals - ) - Collection tasks = [] - Collection 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) - } - -} diff --git a/api/src/main/groovy/com/jessebrault/ssg/SiteSpec.groovy b/api/src/main/groovy/com/jessebrault/ssg/SiteSpec.groovy deleted file mode 100644 index 04c068d..0000000 --- a/api/src/main/groovy/com/jessebrault/ssg/SiteSpec.groovy +++ /dev/null @@ -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 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 })" - } - -} diff --git a/api/src/main/groovy/com/jessebrault/ssg/SsgException.groovy b/api/src/main/groovy/com/jessebrault/ssg/SsgException.groovy new file mode 100644 index 0000000..1bad588 --- /dev/null +++ b/api/src/main/groovy/com/jessebrault/ssg/SsgException.groovy @@ -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) + } + +} diff --git a/api/src/main/groovy/com/jessebrault/ssg/StaticSiteGenerator.groovy b/api/src/main/groovy/com/jessebrault/ssg/StaticSiteGenerator.groovy index f9b0e70..fa6729e 100644 --- a/api/src/main/groovy/com/jessebrault/ssg/StaticSiteGenerator.groovy +++ b/api/src/main/groovy/com/jessebrault/ssg/StaticSiteGenerator.groovy @@ -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 doBuild( + File projectDir, String buildName, - Collection configuratorFactories, - Map buildScriptArgs, - Consumer> diagnosticsConsumer + String buildScriptFqn, + Map buildScriptCliArgs ) -} \ No newline at end of file +} diff --git a/api/src/main/groovy/com/jessebrault/ssg/build/Build.groovy b/api/src/main/groovy/com/jessebrault/ssg/build/Build.groovy deleted file mode 100644 index 0cc64d9..0000000 --- a/api/src/main/groovy/com/jessebrault/ssg/build/Build.groovy +++ /dev/null @@ -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 textsDirs - final NamedSetProvider 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 - } - -} diff --git a/api/src/main/groovy/com/jessebrault/ssg/buildscript/BuildScriptBase.groovy b/api/src/main/groovy/com/jessebrault/ssg/buildscript/BuildScriptBase.groovy index e76e1e1..b56cc0f 100644 --- a/api/src/main/groovy/com/jessebrault/ssg/buildscript/BuildScriptBase.groovy +++ b/api/src/main/groovy/com/jessebrault/ssg/buildscript/BuildScriptBase.groovy @@ -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 } diff --git a/api/src/main/groovy/com/jessebrault/ssg/buildscript/BuildScriptGetter.groovy b/api/src/main/groovy/com/jessebrault/ssg/buildscript/BuildScriptGetter.groovy new file mode 100644 index 0000000..f6f4b26 --- /dev/null +++ b/api/src/main/groovy/com/jessebrault/ssg/buildscript/BuildScriptGetter.groovy @@ -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 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 + def buildScript = scriptClass.getConstructor().newInstance() + buildScript.binding = new Binding(this.scriptCliArgs) + buildScript.projectRoot = projectDir + buildScript.run() + buildScript + } + +} diff --git a/api/src/main/groovy/com/jessebrault/ssg/buildscript/BuildDelegateToBuildSpecConverter.groovy b/api/src/main/groovy/com/jessebrault/ssg/buildscript/BuildScriptToBuildSpecConverter.groovy similarity index 67% rename from api/src/main/groovy/com/jessebrault/ssg/buildscript/BuildDelegateToBuildSpecConverter.groovy rename to api/src/main/groovy/com/jessebrault/ssg/buildscript/BuildScriptToBuildSpecConverter.groovy index 7164e6d..53acaad 100644 --- a/api/src/main/groovy/com/jessebrault/ssg/buildscript/BuildDelegateToBuildSpecConverter.groovy +++ b/api/src/main/groovy/com/jessebrault/ssg/buildscript/BuildScriptToBuildSpecConverter.groovy @@ -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 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 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) + } + } diff --git a/api/src/main/groovy/com/jessebrault/ssg/buildscript/BuildSpec.groovy b/api/src/main/groovy/com/jessebrault/ssg/buildscript/BuildSpec.groovy index 7380cec..1d93ee0 100644 --- a/api/src/main/groovy/com/jessebrault/ssg/buildscript/BuildSpec.groovy +++ b/api/src/main/groovy/com/jessebrault/ssg/buildscript/BuildSpec.groovy @@ -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> basePackages final Provider siteName final Provider baseUrl final Provider outputDir final Provider> globals - final Set> textsDirs + final Provider> models + final Provider> textsDirs + final Provider> textConverters + final Provider 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)" } } diff --git a/api/src/main/groovy/com/jessebrault/ssg/buildscript/FileBuildScriptGetter.groovy b/api/src/main/groovy/com/jessebrault/ssg/buildscript/FileBuildScriptGetter.groovy deleted file mode 100644 index a3283ab..0000000 --- a/api/src/main/groovy/com/jessebrault/ssg/buildscript/FileBuildScriptGetter.groovy +++ /dev/null @@ -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 - } - -} diff --git a/api/src/main/groovy/com/jessebrault/ssg/buildscript/delegates/BuildDelegate.groovy b/api/src/main/groovy/com/jessebrault/ssg/buildscript/delegates/BuildDelegate.groovy index b70a863..43a6df2 100644 --- a/api/src/main/groovy/com/jessebrault/ssg/buildscript/delegates/BuildDelegate.groovy +++ b/api/src/main/groovy/com/jessebrault/ssg/buildscript/delegates/BuildDelegate.groovy @@ -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 withDefaults() { + static Supplier withDefaults(File projectDir) { return { - new BuildDelegate().tap { - outputDir.convention = 'dist' + new BuildDelegate(projectDir).tap { + basePackages.convention = [] as Set + outputDir.convention = new File(projectDir, 'dist') globals.convention = [:] - objectFactory.convention = DefaultRegistryObjectFactory.Builder.withDefaults() + models.convention = [] as Set + textsDirs.convention = [new File(projectDir, 'texts')] as Set + textConverters.convention = [new MarkdownTextConverter()] as Set + objectFactoryBuilder.convention = DefaultRegistryObjectFactory.Builder.withDefaults() } } } - final Property siteName = Property.empty() - final Property baseUrl = Property.empty() - final Property outputDir = Property.empty() - final Property> globals = Property.empty() - final Property objectFactory = Property.empty() + final File projectDir - private final Set> textsDirs = [] + final Property> basePackages = DefaultProperty.>empty(Set) + final Property siteName = DefaultProperty.empty(String) + final Property baseUrl = DefaultProperty.empty(String) + final Property outputDir = DefaultProperty.empty(File) + final Property> globals = DefaultProperty.>empty(Map) + final Property> models = DefaultProperty.>empty(Set) + final Property> textsDirs = DefaultProperty.>empty(Set) + final Property> textConverters = DefaultProperty.>empty(Set) + final Property 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)) } + } + + void model(String name, Class type, Supplier 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 textsDirProvider) { - this.textsDirs << textsDirProvider + void textsDirs(File... textsDirs) { + textsDirs.each { this.textsDir(it) } } - void textsDirs(Set> textsDirProviders) { - textsDirProviders.each { this.textsDir(it) } + void textConverter(TextConverter textConverter) { + this.textConverters.configure { it.add(textConverter) } } - SetProvider getTextsDirs() { - new DefaultSetProvider(this.textsDirs) + void textConverters(TextConverter... textConverters) { + textConverters.each { this.textConverter(it) } + } + + void objectFactoryBuilder(RegistryObjectFactory.Builder builder) { + this.objectFactoryBuilder.set(builder) } } diff --git a/api/src/main/groovy/com/jessebrault/ssg/di/Global.groovy b/api/src/main/groovy/com/jessebrault/ssg/di/Global.groovy new file mode 100644 index 0000000..23c607d --- /dev/null +++ b/api/src/main/groovy/com/jessebrault/ssg/di/Global.groovy @@ -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() +} diff --git a/api/src/main/groovy/com/jessebrault/ssg/di/GlobalsExtension.groovy b/api/src/main/groovy/com/jessebrault/ssg/di/GlobalsExtension.groovy new file mode 100644 index 0000000..46e581d --- /dev/null +++ b/api/src/main/groovy/com/jessebrault/ssg/di/GlobalsExtension.groovy @@ -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 { + + private final GlobalsExtension extension + + @Override + Binding handle(Global global, Class aClass) { + if (extension.globals.containsKey(global.value())) { + return new SingletonBinding(extension.globals.get(global.value()) as T) + } else { + throw new IllegalArgumentException("There is no global for ${global.value()}") + } + } + + } + + final Map globals = [:] + + private final GlobalQualifierHandler globalQualifierHandler = new GlobalQualifierHandler(this) + + @Override + QualifierHandler getQualifierHandler(Class aClass) { + if (Global.is(aClass)) { + return this.globalQualifierHandler as QualifierHandler + } else { + return null + } + } + +} diff --git a/api/src/main/groovy/com/jessebrault/ssg/di/InjectModel.groovy b/api/src/main/groovy/com/jessebrault/ssg/di/InjectModel.groovy new file mode 100644 index 0000000..93c7eb9 --- /dev/null +++ b/api/src/main/groovy/com/jessebrault/ssg/di/InjectModel.groovy @@ -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() +} diff --git a/api/src/main/groovy/com/jessebrault/ssg/di/InjectModelQualifierHandler.groovy b/api/src/main/groovy/com/jessebrault/ssg/di/InjectModelQualifierHandler.groovy new file mode 100644 index 0000000..9f8c5bb --- /dev/null +++ b/api/src/main/groovy/com/jessebrault/ssg/di/InjectModelQualifierHandler.groovy @@ -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 { + + private final ModelsExtension extension + + @Override + Binding handle(InjectModel injectModel, Class 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(found.get() as T) + } + +} diff --git a/api/src/main/groovy/com/jessebrault/ssg/di/InjectModels.groovy b/api/src/main/groovy/com/jessebrault/ssg/di/InjectModels.groovy new file mode 100644 index 0000000..84c945d --- /dev/null +++ b/api/src/main/groovy/com/jessebrault/ssg/di/InjectModels.groovy @@ -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() +} diff --git a/api/src/main/groovy/com/jessebrault/ssg/di/InjectModelsQualifierHandler.groovy b/api/src/main/groovy/com/jessebrault/ssg/di/InjectModelsQualifierHandler.groovy new file mode 100644 index 0000000..033ef55 --- /dev/null +++ b/api/src/main/groovy/com/jessebrault/ssg/di/InjectModelsQualifierHandler.groovy @@ -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 { + + private final ModelsExtension extension + + @Override + Binding handle(InjectModels injectModels, Class 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(allFound as T) + } + +} diff --git a/api/src/main/groovy/com/jessebrault/ssg/objects/InjectPage.groovy b/api/src/main/groovy/com/jessebrault/ssg/di/InjectPage.groovy similarity index 92% rename from api/src/main/groovy/com/jessebrault/ssg/objects/InjectPage.groovy rename to api/src/main/groovy/com/jessebrault/ssg/di/InjectPage.groovy index bc6ddcf..c472e79 100644 --- a/api/src/main/groovy/com/jessebrault/ssg/objects/InjectPage.groovy +++ b/api/src/main/groovy/com/jessebrault/ssg/di/InjectPage.groovy @@ -1,4 +1,4 @@ -package com.jessebrault.ssg.objects +package com.jessebrault.ssg.di import jakarta.inject.Qualifier diff --git a/api/src/main/groovy/com/jessebrault/ssg/objects/InjectPageQualifierHandler.groovy b/api/src/main/groovy/com/jessebrault/ssg/di/InjectPageQualifierHandler.groovy similarity index 87% rename from api/src/main/groovy/com/jessebrault/ssg/objects/InjectPageQualifierHandler.groovy rename to api/src/main/groovy/com/jessebrault/ssg/di/InjectPageQualifierHandler.groovy index 862562b..2f45ef2 100644 --- a/api/src/main/groovy/com/jessebrault/ssg/objects/InjectPageQualifierHandler.groovy +++ b/api/src/main/groovy/com/jessebrault/ssg/di/InjectPageQualifierHandler.groovy @@ -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 { 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 { if (found == null) { throw new IllegalArgumentException("Cannot find a page with the following name or path: $requested") } - new SingletonBinding(found.get() as T) + new SingletonBinding(found as T) } } diff --git a/api/src/main/groovy/com/jessebrault/ssg/objects/InjectPages.groovy b/api/src/main/groovy/com/jessebrault/ssg/di/InjectPages.groovy similarity index 92% rename from api/src/main/groovy/com/jessebrault/ssg/objects/InjectPages.groovy rename to api/src/main/groovy/com/jessebrault/ssg/di/InjectPages.groovy index b74d09d..0cf96ed 100644 --- a/api/src/main/groovy/com/jessebrault/ssg/objects/InjectPages.groovy +++ b/api/src/main/groovy/com/jessebrault/ssg/di/InjectPages.groovy @@ -1,4 +1,4 @@ -package com.jessebrault.ssg.objects +package com.jessebrault.ssg.di import jakarta.inject.Qualifier diff --git a/api/src/main/groovy/com/jessebrault/ssg/objects/InjectPagesQualifierHandler.groovy b/api/src/main/groovy/com/jessebrault/ssg/di/InjectPagesQualifierHandler.groovy similarity index 76% rename from api/src/main/groovy/com/jessebrault/ssg/objects/InjectPagesQualifierHandler.groovy rename to api/src/main/groovy/com/jessebrault/ssg/di/InjectPagesQualifierHandler.groovy index 1c3a5c1..adf5174 100644 --- a/api/src/main/groovy/com/jessebrault/ssg/objects/InjectPagesQualifierHandler.groovy +++ b/api/src/main/groovy/com/jessebrault/ssg/di/InjectPagesQualifierHandler.groovy @@ -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 { for (final String requested : injectPages.value()) { if (requested.startsWith('/')) { def glob = new Glob(requested) - def allFound = this.pagesExtension.pageProviders.inject([] as Set) { acc, pageProvider -> - if (glob.matches(pageProvider.path)) { - acc << pageProvider.get() + def allFound = this.pagesExtension.allPages.inject([] as Set) { 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(foundPages as T) diff --git a/api/src/main/groovy/com/jessebrault/ssg/objects/InjectText.groovy b/api/src/main/groovy/com/jessebrault/ssg/di/InjectText.groovy similarity index 92% rename from api/src/main/groovy/com/jessebrault/ssg/objects/InjectText.groovy rename to api/src/main/groovy/com/jessebrault/ssg/di/InjectText.groovy index c19c9b5..3f4d66e 100644 --- a/api/src/main/groovy/com/jessebrault/ssg/objects/InjectText.groovy +++ b/api/src/main/groovy/com/jessebrault/ssg/di/InjectText.groovy @@ -1,4 +1,4 @@ -package com.jessebrault.ssg.objects +package com.jessebrault.ssg.di import jakarta.inject.Qualifier diff --git a/api/src/main/groovy/com/jessebrault/ssg/di/InjectTextQualifierHandler.groovy b/api/src/main/groovy/com/jessebrault/ssg/di/InjectTextQualifierHandler.groovy new file mode 100644 index 0000000..1c98ecc --- /dev/null +++ b/api/src/main/groovy/com/jessebrault/ssg/di/InjectTextQualifierHandler.groovy @@ -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 { + + private final TextsExtension extension + + @Override + Binding handle(InjectText injectText, Class 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(found as T) + } + +} diff --git a/api/src/main/groovy/com/jessebrault/ssg/objects/InjectTexts.groovy b/api/src/main/groovy/com/jessebrault/ssg/di/InjectTexts.groovy similarity index 92% rename from api/src/main/groovy/com/jessebrault/ssg/objects/InjectTexts.groovy rename to api/src/main/groovy/com/jessebrault/ssg/di/InjectTexts.groovy index b6bbf89..7642e26 100644 --- a/api/src/main/groovy/com/jessebrault/ssg/objects/InjectTexts.groovy +++ b/api/src/main/groovy/com/jessebrault/ssg/di/InjectTexts.groovy @@ -1,4 +1,4 @@ -package com.jessebrault.ssg.objects +package com.jessebrault.ssg.di import jakarta.inject.Qualifier diff --git a/api/src/main/groovy/com/jessebrault/ssg/di/InjectTextsQualifierHandler.groovy b/api/src/main/groovy/com/jessebrault/ssg/di/InjectTextsQualifierHandler.groovy new file mode 100644 index 0000000..63961ff --- /dev/null +++ b/api/src/main/groovy/com/jessebrault/ssg/di/InjectTextsQualifierHandler.groovy @@ -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 { + + private final TextsExtension extension + + @Override + Binding handle(InjectTexts injectTexts, Class aClass) { + if (!Set.is(aClass)) { + throw new IllegalArgumentException('Cannot @InjectTexts on a non-Set parameter/method/field.') + } + def allFound = injectTexts.value().inject([] as Set) { acc, nameOrPathGlob -> + if (nameOrPathGlob.startsWith('/')) { + def glob = new Glob(nameOrPathGlob) + def matching = this.extension.allTexts.inject([] as Set) { 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(allFound as T) + } + +} diff --git a/api/src/main/groovy/com/jessebrault/ssg/di/ModelsExtension.groovy b/api/src/main/groovy/com/jessebrault/ssg/di/ModelsExtension.groovy new file mode 100644 index 0000000..b65d97d --- /dev/null +++ b/api/src/main/groovy/com/jessebrault/ssg/di/ModelsExtension.groovy @@ -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 allModels = [] + + private final QualifierHandler injectModelQualifierHandler = new InjectModelQualifierHandler(this) + private final QualifierHandler injectModelsQualifierHandler = new InjectModelsQualifierHandler(this) + + @Override + QualifierHandler getQualifierHandler(Class aClass) { + if (aClass == InjectModel) { + return this.injectModelQualifierHandler as QualifierHandler + } else if (aClass == InjectModels) { + return this.injectModelsQualifierHandler as QualifierHandler + } else { + return null + } + } + +} diff --git a/api/src/main/groovy/com/jessebrault/ssg/objects/PagesExtension.groovy b/api/src/main/groovy/com/jessebrault/ssg/di/PagesExtension.groovy similarity index 85% rename from api/src/main/groovy/com/jessebrault/ssg/objects/PagesExtension.groovy rename to api/src/main/groovy/com/jessebrault/ssg/di/PagesExtension.groovy index 3833888..b6491d5 100644 --- a/api/src/main/groovy/com/jessebrault/ssg/objects/PagesExtension.groovy +++ b/api/src/main/groovy/com/jessebrault/ssg/di/PagesExtension.groovy @@ -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 pageProviders = [] + final Set allPages = [] private final QualifierHandler injectPage = new InjectPageQualifierHandler(this) private final QualifierHandler injectPages = new InjectPagesQualifierHandler(this) diff --git a/api/src/main/groovy/com/jessebrault/ssg/di/SelfPage.groovy b/api/src/main/groovy/com/jessebrault/ssg/di/SelfPage.groovy new file mode 100644 index 0000000..4868434 --- /dev/null +++ b/api/src/main/groovy/com/jessebrault/ssg/di/SelfPage.groovy @@ -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 {} diff --git a/api/src/main/groovy/com/jessebrault/ssg/di/SelfPageExtension.groovy b/api/src/main/groovy/com/jessebrault/ssg/di/SelfPageExtension.groovy new file mode 100644 index 0000000..d8f6537 --- /dev/null +++ b/api/src/main/groovy/com/jessebrault/ssg/di/SelfPageExtension.groovy @@ -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 { + + private final SelfPageExtension extension + + @Override + Binding handle(SelfPage selfPage, Class 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(this.extension.currentPage as T) + } + + } + + Page currentPage + + private final SelfPageQualifierHandler selfPageQualifierHandler = new SelfPageQualifierHandler(this) + + @Override + QualifierHandler getQualifierHandler(Class annotationType) { + if (SelfPage.is(annotationType)) { + return this.selfPageQualifierHandler as QualifierHandler + } else { + return null + } + } + +} diff --git a/api/src/main/groovy/com/jessebrault/ssg/objects/SsgObjectFactory.groovy b/api/src/main/groovy/com/jessebrault/ssg/di/SsgObjectFactory.groovy similarity index 92% rename from api/src/main/groovy/com/jessebrault/ssg/objects/SsgObjectFactory.groovy rename to api/src/main/groovy/com/jessebrault/ssg/di/SsgObjectFactory.groovy index e6a690c..6e071bd 100644 --- a/api/src/main/groovy/com/jessebrault/ssg/objects/SsgObjectFactory.groovy +++ b/api/src/main/groovy/com/jessebrault/ssg/di/SsgObjectFactory.groovy @@ -1,4 +1,4 @@ -package com.jessebrault.ssg.objects +package com.jessebrault.ssg.di import groowt.util.di.DefaultRegistryObjectFactory import groowt.util.di.RegistryObjectFactory diff --git a/api/src/main/groovy/com/jessebrault/ssg/di/TextsExtension.groovy b/api/src/main/groovy/com/jessebrault/ssg/di/TextsExtension.groovy new file mode 100644 index 0000000..d74f823 --- /dev/null +++ b/api/src/main/groovy/com/jessebrault/ssg/di/TextsExtension.groovy @@ -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 allTexts = [] + + private final QualifierHandler injectTextQualifierHandler = new InjectTextQualifierHandler(this) + private final QualifierHandler injectTextsQualifierHandler = new InjectTextsQualifierHandler(this) + + @Override + QualifierHandler getQualifierHandler(Class aClass) { + if (InjectText.is(aClass)) { + return this.injectTextQualifierHandler as QualifierHandler + } else if (InjectTexts.is(aClass)) { + return this.injectTextsQualifierHandler as QualifierHandler + } else { + return null + } + } + +} diff --git a/api/src/main/groovy/com/jessebrault/ssg/dsl/EmbeddablePart.groovy b/api/src/main/groovy/com/jessebrault/ssg/dsl/EmbeddablePart.groovy deleted file mode 100644 index c54dcb7..0000000 --- a/api/src/main/groovy/com/jessebrault/ssg/dsl/EmbeddablePart.groovy +++ /dev/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> diagnosticsConsumer - - @Nullable - private final Text text - - EmbeddablePart( - Part part, - RenderContext context, - Consumer> 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 })" - } - -} diff --git a/api/src/main/groovy/com/jessebrault/ssg/dsl/EmbeddablePartsMap.groovy b/api/src/main/groovy/com/jessebrault/ssg/dsl/EmbeddablePartsMap.groovy deleted file mode 100644 index bc4878a..0000000 --- a/api/src/main/groovy/com/jessebrault/ssg/dsl/EmbeddablePartsMap.groovy +++ /dev/null @@ -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 partsMap = [:] - - EmbeddablePartsMap( - RenderContext context, - Consumer> 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 })" - } - -} diff --git a/api/src/main/groovy/com/jessebrault/ssg/dsl/EmbeddableText.groovy b/api/src/main/groovy/com/jessebrault/ssg/dsl/EmbeddableText.groovy deleted file mode 100644 index 77f3f6b..0000000 --- a/api/src/main/groovy/com/jessebrault/ssg/dsl/EmbeddableText.groovy +++ /dev/null @@ -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> 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 })" - } - -} diff --git a/api/src/main/groovy/com/jessebrault/ssg/dsl/EmbeddableTextsCollection.groovy b/api/src/main/groovy/com/jessebrault/ssg/dsl/EmbeddableTextsCollection.groovy deleted file mode 100644 index 7165db2..0000000 --- a/api/src/main/groovy/com/jessebrault/ssg/dsl/EmbeddableTextsCollection.groovy +++ /dev/null @@ -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 embeddableTexts = [] - - EmbeddableTextsCollection(Collection texts, Consumer> diagnosticsConsumer) { - texts.each { - this << new EmbeddableText(it, diagnosticsConsumer) - } - } - - @Override - String toString() { - "EmbeddableTextsCollection(embeddableTexts: ${ this.embeddableTexts })" - } - -} diff --git a/api/src/main/groovy/com/jessebrault/ssg/dsl/ModelCollection.groovy b/api/src/main/groovy/com/jessebrault/ssg/dsl/ModelCollection.groovy deleted file mode 100644 index 2a6987f..0000000 --- a/api/src/main/groovy/com/jessebrault/ssg/dsl/ModelCollection.groovy +++ /dev/null @@ -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 { - - 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> models = [] - - ModelCollection(Collection> models) { - this.models.addAll(models) - } - - @Nullable - Model getByName(String name) { - this.models.find { it.name == name } - } - - @Nullable - Model getByNameAndType(String name, Class type) { - this.models.find { it.name == name && type.isAssignableFrom(it.get().class) } as Model - } - - Optional> findByName(String name) { - Optional.ofNullable(this.getByName(name)) - } - - def Optional> findByNameAndType(String name, Class type) { - Optional.ofNullable(this.getByNameAndType(name, type)) - } - - def ModelCollection findAllByType(Class 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 : null - } - def result = new ModelCollection<>(es) - logger.trace(exit, 'result: {}', result) - result - } - - def Optional> findOne(Class type, Predicate 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) - } - - def Optional> findOne(Class type) { - this.findOne(type) { true } - } - - Optional> findOne(Predicate filter) { - Optional.ofNullable(this.models.find { - filter.test(it.get()) ? it : null - }) - } - -} diff --git a/api/src/main/groovy/com/jessebrault/ssg/dsl/StandardDslMap.groovy b/api/src/main/groovy/com/jessebrault/ssg/dsl/StandardDslMap.groovy deleted file mode 100644 index 40d986c..0000000 --- a/api/src/main/groovy/com/jessebrault/ssg/dsl/StandardDslMap.groovy +++ /dev/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 custom = [:] - - Consumer> diagnosticsConsumer = { } - String loggerName = '' - Text text = null - - void putCustom(String key, Object value) { - this.custom.put(key, value) - } - - void putAllCustom(Map m) { - this.custom.putAll(m) - } - - } - - static Map get( - RenderContext context, - Consumer 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(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) - } - } - -} diff --git a/api/src/main/groovy/com/jessebrault/ssg/dsl/TaskCollection.groovy b/api/src/main/groovy/com/jessebrault/ssg/dsl/TaskCollection.groovy deleted file mode 100644 index 9d405d7..0000000 --- a/api/src/main/groovy/com/jessebrault/ssg/dsl/TaskCollection.groovy +++ /dev/null @@ -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 tasks - - TaskCollection(Collection src = []) { - this.tasks = [] - this.tasks.addAll(src) - } - - def Collection byType(Class taskClass) { - this.tasks.findAll { taskClass.isAssignableFrom(it.class) } as Collection - } - -} diff --git a/api/src/main/groovy/com/jessebrault/ssg/dsl/tagbuilder/DynamicTagBuilder.groovy b/api/src/main/groovy/com/jessebrault/ssg/dsl/tagbuilder/DynamicTagBuilder.groovy deleted file mode 100644 index 026cd2d..0000000 --- a/api/src/main/groovy/com/jessebrault/ssg/dsl/tagbuilder/DynamicTagBuilder.groovy +++ /dev/null @@ -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 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" - } - - @Override - String create(String name, Map 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" - } - - @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) - } - } - -} diff --git a/api/src/main/groovy/com/jessebrault/ssg/dsl/tagbuilder/TagBuilder.groovy b/api/src/main/groovy/com/jessebrault/ssg/dsl/tagbuilder/TagBuilder.groovy deleted file mode 100644 index 1abb821..0000000 --- a/api/src/main/groovy/com/jessebrault/ssg/dsl/tagbuilder/TagBuilder.groovy +++ /dev/null @@ -1,8 +0,0 @@ -package com.jessebrault.ssg.dsl.tagbuilder - -interface TagBuilder { - String create(String name) - String create(String name, Map attributes) - String create(String name, String body) - String create(String name, Map attributes, String body) -} diff --git a/api/src/main/groovy/com/jessebrault/ssg/html/AbstractHtmlTask.groovy b/api/src/main/groovy/com/jessebrault/ssg/html/AbstractHtmlTask.groovy deleted file mode 100644 index 10f07f0..0000000 --- a/api/src/main/groovy/com/jessebrault/ssg/html/AbstractHtmlTask.groovy +++ /dev/null @@ -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 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 transform(Collection allTasks) - - @Override - final Collection execute(Collection 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() })" - } - -} diff --git a/api/src/main/groovy/com/jessebrault/ssg/html/HtmlOutput.groovy b/api/src/main/groovy/com/jessebrault/ssg/html/HtmlOutput.groovy deleted file mode 100644 index 4fa1b30..0000000 --- a/api/src/main/groovy/com/jessebrault/ssg/html/HtmlOutput.groovy +++ /dev/null @@ -1,7 +0,0 @@ -package com.jessebrault.ssg.html - -import com.jessebrault.ssg.task.FileOutput - -interface HtmlOutput extends FileOutput { - String getHtmlPath() -} \ No newline at end of file diff --git a/api/src/main/groovy/com/jessebrault/ssg/html/HtmlTask.groovy b/api/src/main/groovy/com/jessebrault/ssg/html/HtmlTask.groovy deleted file mode 100644 index 9486cb2..0000000 --- a/api/src/main/groovy/com/jessebrault/ssg/html/HtmlTask.groovy +++ /dev/null @@ -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() -} \ No newline at end of file diff --git a/api/src/main/groovy/com/jessebrault/ssg/html/ModelToHtmlSpec.groovy b/api/src/main/groovy/com/jessebrault/ssg/html/ModelToHtmlSpec.groovy deleted file mode 100644 index 2caf30e..0000000 --- a/api/src/main/groovy/com/jessebrault/ssg/html/ModelToHtmlSpec.groovy +++ /dev/null @@ -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 { - final Model model - final Template template - final String path -} diff --git a/api/src/main/groovy/com/jessebrault/ssg/html/ModelToHtmlTask.groovy b/api/src/main/groovy/com/jessebrault/ssg/html/ModelToHtmlTask.groovy deleted file mode 100644 index e170b9f..0000000 --- a/api/src/main/groovy/com/jessebrault/ssg/html/ModelToHtmlTask.groovy +++ /dev/null @@ -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 extends AbstractHtmlTask> { - - private final SiteSpec siteSpec - private final Map globals - private final Model model - private final Template template - private final Collection allTexts - private final Collection> allModels - private final Collection allParts - - ModelToHtmlTask( - String relativeHtmlPath, - TaskSpec taskSpec, - Model model, - Template template, - Collection allTexts, - Collection> allModels, - Collection 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 transform(Collection 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() })" - } - -} diff --git a/api/src/main/groovy/com/jessebrault/ssg/html/ModelToHtmlTaskFactory.groovy b/api/src/main/groovy/com/jessebrault/ssg/html/ModelToHtmlTaskFactory.groovy deleted file mode 100644 index 921124e..0000000 --- a/api/src/main/groovy/com/jessebrault/ssg/html/ModelToHtmlTaskFactory.groovy +++ /dev/null @@ -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 extends AbstractRenderTaskFactory { - - CollectionProvider> specsProvider = CollectionProviders.getEmpty() - - @Override - Result> 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 - ) - }) - } - -} diff --git a/api/src/main/groovy/com/jessebrault/ssg/html/PageToHtmlSpec.groovy b/api/src/main/groovy/com/jessebrault/ssg/html/PageToHtmlSpec.groovy deleted file mode 100644 index 869f101..0000000 --- a/api/src/main/groovy/com/jessebrault/ssg/html/PageToHtmlSpec.groovy +++ /dev/null @@ -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 toRelativeHtmlPath -} diff --git a/api/src/main/groovy/com/jessebrault/ssg/html/PageToHtmlSpecProviders.groovy b/api/src/main/groovy/com/jessebrault/ssg/html/PageToHtmlSpecProviders.groovy deleted file mode 100644 index 09bfd82..0000000 --- a/api/src/main/groovy/com/jessebrault/ssg/html/PageToHtmlSpecProviders.groovy +++ /dev/null @@ -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 from(CollectionProvider pagesProvider) { - CollectionProviders.fromCollection(pagesProvider.provide().collect { Page page -> - new PageToHtmlSpec(page, { TaskSpec taskSpec -> - ExtensionUtil.stripExtension(page.path) + '.html' - }) - }) - } - - static CollectionProvider from( - CollectionProvider pagesProvider, - Function> toRelativeHtmlPath - ) { - CollectionProviders.fromCollection(pagesProvider.provide().collect { - new PageToHtmlSpec(it, toRelativeHtmlPath.apply(it)) - }) - } - - private PageToHtmlSpecProviders() {} - -} diff --git a/api/src/main/groovy/com/jessebrault/ssg/html/PageToHtmlTask.groovy b/api/src/main/groovy/com/jessebrault/ssg/html/PageToHtmlTask.groovy deleted file mode 100644 index da99946..0000000 --- a/api/src/main/groovy/com/jessebrault/ssg/html/PageToHtmlTask.groovy +++ /dev/null @@ -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 { - - private final SiteSpec siteSpec - private final Map globals - private final Page page - private final Collection allTexts - private final Collection> allModels - private final Collection allParts - - PageToHtmlTask( - String relativeHtmlPath, - TaskSpec taskSpec, - Page page, - Collection allTexts, - Collection> allModels, - Collection 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 transform(Collection 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() })" - } - -} diff --git a/api/src/main/groovy/com/jessebrault/ssg/html/PageToHtmlTaskFactory.groovy b/api/src/main/groovy/com/jessebrault/ssg/html/PageToHtmlTaskFactory.groovy deleted file mode 100644 index 6a440f4..0000000 --- a/api/src/main/groovy/com/jessebrault/ssg/html/PageToHtmlTaskFactory.groovy +++ /dev/null @@ -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 specsProvider = CollectionProviders.getEmpty() - - @Override - Result> 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 tasks = this.specsProvider.provide() - .collect { - new PageToHtmlTask( - it.toRelativeHtmlPath.apply(taskSpec), - taskSpec, - it.page, - allTexts, - allModels, - allParts - ) - } - Result.of(tasks) - } -} diff --git a/api/src/main/groovy/com/jessebrault/ssg/html/SimpleHtmlOutput.groovy b/api/src/main/groovy/com/jessebrault/ssg/html/SimpleHtmlOutput.groovy deleted file mode 100644 index 235df51..0000000 --- a/api/src/main/groovy/com/jessebrault/ssg/html/SimpleHtmlOutput.groovy +++ /dev/null @@ -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 -} diff --git a/api/src/main/groovy/com/jessebrault/ssg/html/TextToHtmlSpec.groovy b/api/src/main/groovy/com/jessebrault/ssg/html/TextToHtmlSpec.groovy deleted file mode 100644 index e7215e4..0000000 --- a/api/src/main/groovy/com/jessebrault/ssg/html/TextToHtmlSpec.groovy +++ /dev/null @@ -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 toRelativeHtmlPath -} diff --git a/api/src/main/groovy/com/jessebrault/ssg/html/TextToHtmlSpecProviders.groovy b/api/src/main/groovy/com/jessebrault/ssg/html/TextToHtmlSpecProviders.groovy deleted file mode 100644 index 51c2df5..0000000 --- a/api/src/main/groovy/com/jessebrault/ssg/html/TextToHtmlSpecProviders.groovy +++ /dev/null @@ -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> from(SourceProviders sources) { - from(sources) { text -> - return { TaskSpec taskSpec -> - ExtensionUtil.stripExtension(text.path) + '.html' - } - } - } - - static CollectionProvider> from( - SourceProviders sources, - Function> toRelativeHtmlPath - ) { - from(sources.textsProvider, sources.templatesProvider, toRelativeHtmlPath) - } - - static CollectionProvider> from( - CollectionProvider textsProvider, - CollectionProvider