Overhaul for 0.7.0-SNAPSHOT.

This commit is contained in:
Jesse Brault 2026-01-04 11:43:18 -06:00
parent 52dc26c450
commit ba821b11c2
43 changed files with 1074 additions and 82 deletions

View File

@ -8,7 +8,8 @@ updated to the same version in `cli/build.gradle`.
## Version-bumping ## Version-bumping
Update the version of the project in `buildSrc/src/main/groovy/ssg-common.gradle`. Then update the references to the Update the version of the project in `buildSrc/src/main/groovy/ssg-common.gradle`. Then update the references to the
`cli` and `api` projects in `ssg-gradle-plugin/src/main/java/com/jessebrault/ssg/gradle/SsgGradlePlugin.java`. `cli` and `api` projects in `ssg-gradle-plugin/src/main/java/com/jessebrault/ssg/gradle/SsgGradlePlugin.java`. Finally,
update the version in the `cli` project for the cli info message.
## Publishing ## Publishing

View File

@ -0,0 +1,10 @@
package com.jessebrault.ssg;
import groowt.view.component.web.WebViewComponent;
import io.github.classgraph.ScanResult;
import java.util.Set;
public interface ComponentClassScanner {
Set<Class<? extends WebViewComponent>> getWebViewComponentClasses(ScanResult scanResult);
}

View File

@ -0,0 +1,15 @@
package com.jessebrault.ssg;
import com.jessebrault.ssg.page.Page;
import java.io.File;
public class ConsolePageWriter implements PageWriter {
@Override
public void write(Page page, File outputDir, String renderedPage) {
System.out.println("--- Page " + page.getPath() + " ---");
System.out.println(renderedPage);
}
}

View File

@ -0,0 +1,41 @@
package com.jessebrault.ssg;
import groowt.view.component.web.WebViewComponent;
import io.github.classgraph.ClassInfoList;
import io.github.classgraph.ScanResult;
import jakarta.inject.Inject;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
public class DefaultComponentClassScanner implements ComponentClassScanner {
private final ExecutorService executorService;
@Inject
public DefaultComponentClassScanner(ExecutorService executorService) {
this.executorService = executorService;
}
@Override
public Set<Class<? extends WebViewComponent>> getWebViewComponentClasses(ScanResult scanResult) {
final ClassInfoList classInfoList = scanResult.getClassesImplementing(WebViewComponent.class);
final Set<Class<? extends WebViewComponent>> results = ConcurrentHashMap.newKeySet();
// fork
final List<CompletableFuture<Void>> futures = classInfoList.stream().map(classInfo ->
CompletableFuture.runAsync(() ->
results.add(classInfo.loadClass(WebViewComponent.class)),
executorService
)).toList();
// join
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
return results;
}
}

View File

@ -0,0 +1,57 @@
package com.jessebrault.ssg;
import com.jessebrault.di.RegistryObjectFactory;
import com.jessebrault.ssg.buildscript.BuildSpec;
import com.jessebrault.ssg.di.GlobalsExtension;
import com.jessebrault.ssg.di.ModelsExtension;
import com.jessebrault.ssg.di.TextsExtension;
import jakarta.inject.Inject;
import static com.jessebrault.di.BindingUtil.named;
import static com.jessebrault.di.BindingUtil.toSingleton;
public class DefaultObjectFactoryConfigurator implements ObjectFactoryConfigurator {
private final TextsGetter textsGetter;
@Inject
public DefaultObjectFactoryConfigurator(TextsGetter textsGetter) {
this.textsGetter = textsGetter;
}
@Override
public void configure(RegistryObjectFactory registryObjectFactory, BuildSpec buildSpec) {
registryObjectFactory.configureRegistry(registry -> {
// texts
final var textsExtension = new TextsExtension();
textsExtension.getAllTexts().addAll(this.textsGetter.getTexts(buildSpec));
registry.addExtension(textsExtension);
// models
final var modelsExtension = new ModelsExtension();
modelsExtension.getAllModels().addAll(buildSpec.getModels().get(() ->
new SsgException("the models Property in " + buildSpec.getName()
+ " must contain at least an empty set.")
));
registry.addExtension(modelsExtension);
// globals
final var globalsExtension = new GlobalsExtension();
globalsExtension.getGlobals().putAll(buildSpec.getGlobals().get(() ->
new SsgException("the globals Property in " + buildSpec.getName()
+ " must contain at least an empty set.")
));
registry.addExtension(globalsExtension);
// various others
registry.bind(named("buildName", String.class), toSingleton(buildSpec.getName()));
registry.bind(named("siteName", String.class), toSingleton(buildSpec.getSiteName().get(() ->
new SsgException("the siteName Property in " + buildSpec.getName() + " must be set.")
)));
registry.bind(named("baseUrl", String.class), toSingleton(buildSpec.getBaseUrl().get(() ->
new SsgException("the baseUrl Property in " + buildSpec.getName() + " must be set.")
)));
});
}
}

View File

@ -0,0 +1,68 @@
package com.jessebrault.ssg
import com.jessebrault.di.ObjectFactory
import com.jessebrault.ssg.view.SkipTemplate
import com.jessebrault.ssg.view.WvcCompiler
import com.jessebrault.ssg.view.WvcPageView
import groowt.view.component.factory.ComponentFactories
import groowt.view.component.web.DefaultWebViewComponentContext
import groowt.view.component.web.WebViewComponent
import groowt.view.component.web.WebViewComponentContext
import groowt.view.component.web.WebViewComponentScope
class DefaultPageContextFactory implements PageContextFactory {
protected WebViewComponent makeComponent(
ObjectFactory objectFactory,
Class<? extends WebViewComponent> wvcClass,
Map attr,
Object[] args
) {
if (!attr.isEmpty() && args.length > 0) {
return objectFactory.createInstance(wvcClass, attr, *args)
} else if (!attr.isEmpty()) {
return objectFactory.createInstance(wvcClass, attr)
} else if (args.length > 0) {
return objectFactory.createInstance(wvcClass, *args)
} else {
return objectFactory.createInstance(wvcClass)
}
}
@Override
WebViewComponentContext makeContext(
WvcPageView wvcPageView,
ObjectFactory buildObjectFactory,
Set<Class<? extends WebViewComponent>> allWvcClasses
) {
new DefaultWebViewComponentContext().tap {
configureRootScope(WebViewComponentScope) {
// custom components
allWvcClasses.each { wvcClass ->
//noinspection GroovyAssignabilityCheck
add(wvcClass, ComponentFactories.ofClosureClassType(wvcClass) { Map attr, Object[] args ->
// instantiate component and set context
WebViewComponent component = makeComponent(buildObjectFactory, wvcClass, attr, args)
component.context = wvcPageView.context
// set the template
if (component.componentTemplate == null && !wvcClass.isAnnotationPresent(SkipTemplate)) {
def compileResult = buildObjectFactory.createInstance(WvcCompiler).compileTemplate(
wvcClass,
wvcClass.simpleName + 'Template.wvc'
)
if (compileResult.isRight()) {
component.componentTemplate = compileResult.getRight()
} else {
def left = compileResult.getLeft()
throw new RuntimeException(left.message, left.exception)
}
}
return component
})
}
}
}
}
}

View File

@ -0,0 +1,62 @@
package com.jessebrault.ssg;
import com.jessebrault.di.ObjectFactory;
import com.jessebrault.fp.either.Either;
import com.jessebrault.ssg.page.Page;
import com.jessebrault.ssg.util.Diagnostic;
import com.jessebrault.ssg.view.PageView;
import com.jessebrault.ssg.view.WvcPageView;
import groowt.view.component.web.WebViewComponent;
import jakarta.inject.Inject;
import java.io.StringWriter;
import java.util.Set;
public class DefaultPageRenderer implements PageRenderer {
private final PageContextFactory pageContextFactory;
@Inject
public DefaultPageRenderer(PageContextFactory pageContextFactory) {
this.pageContextFactory = pageContextFactory;
}
@Override
public Either<Diagnostic, String> renderPage(
Page page,
String baseUrl,
ObjectFactory buildObjectFactory,
Set<Class<? extends WebViewComponent>> allWvcClasses
) {
// create the view
final Either<Diagnostic, PageView> viewResult = page.createView();
if (viewResult.isLeft()) {
return Either.left(viewResult.getLeft());
}
final PageView pageView = viewResult.getRight();
// prepare for rendering
// set props
pageView.setPageTitle(page.getName());
pageView.setUrl(baseUrl + page.getPath());
// set context if WvcPageView
if (pageView instanceof WvcPageView wvcPageView) {
wvcPageView.setContext(this.pageContextFactory.makeContext(wvcPageView, buildObjectFactory, allWvcClasses));
}
// Render page
final var sw = new StringWriter();
try {
pageView.renderTo(sw);
} catch (Exception exception) {
return Either.left(new Diagnostic(
"There was an exception while rendering " + page.getName() + " as " + pageView.getClass().getName(),
exception
));
}
return Either.right(sw.toString());
}
}

View File

@ -0,0 +1,80 @@
package com.jessebrault.ssg;
import com.jessebrault.di.ObjectFactory;
import com.jessebrault.ssg.page.DefaultWvcPage;
import com.jessebrault.ssg.page.Page;
import com.jessebrault.ssg.page.PageFactory;
import com.jessebrault.ssg.page.PageSpec;
import com.jessebrault.ssg.view.PageView;
import com.jessebrault.ssg.view.WvcCompiler;
import io.github.classgraph.AnnotationInfo;
import io.github.classgraph.ClassInfoList;
import io.github.classgraph.ScanResult;
import jakarta.inject.Inject;
import java.util.*;
import java.util.concurrent.*;
public class DefaultPageScanner implements PageScanner {
private final ExecutorService executorService;
private final WvcCompiler wvcCompiler;
@Inject
public DefaultPageScanner(ExecutorService executorService, WvcCompiler wvcCompiler) {
this.executorService = executorService;
this.wvcCompiler = wvcCompiler;
}
@Override
public Set<Page> getAllPages(ScanResult scanResult, ObjectFactory buildObjectFactory) {
final Set<Page> results = ConcurrentHashMap.newKeySet();
// Start fetching single pages
final ClassInfoList pageViewInfoList = scanResult.getClassesImplementing(PageView.class);
final List<CompletableFuture<Void>> pageViewFutures = pageViewInfoList.stream()
.map(classInfo -> {
return CompletableFuture.runAsync(() -> {
final AnnotationInfo annotationInfo = classInfo.getAnnotationInfo(PageSpec.class);
if (annotationInfo != null) {
final PageSpec pageSpec = (PageSpec) annotationInfo.loadClassAndInstantiate();
results.add(new DefaultWvcPage(Map.of(
"name", pageSpec.name(),
"path", pageSpec.path(),
"fileExtension", pageSpec.fileExtension(),
"viewType", classInfo.loadClass(),
"templateResource", pageSpec.templateResource().isEmpty()
? classInfo.getSimpleName() + "Template.wvc"
: pageSpec.templateResource(),
"objectFactory", buildObjectFactory,
"wvcCompiler", this.wvcCompiler
)));
}
}, this.executorService);
})
.toList();
// Start fetching page factories
final ClassInfoList pageFactoryInfoList = scanResult.getClassesImplementing(PageFactory.class);
final List<CompletableFuture<Void>> pageFactoryFutures = pageFactoryInfoList.stream()
.map(classInfo -> {
return CompletableFuture.runAsync(() -> {
final Class<? extends PageFactory> pageFactoryClass = classInfo.loadClass(PageFactory.class);
final PageFactory pageFactory = buildObjectFactory.createInstance(pageFactoryClass);
final Collection<Page> pages = pageFactory.create();
results.addAll(pages);
}, this.executorService);
})
.toList();
// join
final List<CompletableFuture<Void>> allFutures = new ArrayList<>();
allFutures.addAll(pageViewFutures);
allFutures.addAll(pageFactoryFutures);
CompletableFuture.allOf(allFutures.toArray(new CompletableFuture[0])).join();
return results;
}
}

View File

@ -183,8 +183,6 @@ class DefaultStaticSiteGenerator implements StaticSiteGenerator {
@Override @Override
Collection<Diagnostic> doBuild( Collection<Diagnostic> doBuild(
File projectDir,
String buildName,
String buildScriptFqn, String buildScriptFqn,
Map<String, String> buildScriptCliArgs Map<String, String> buildScriptCliArgs
) { ) {

View File

@ -0,0 +1,25 @@
package com.jessebrault.ssg;
import com.jessebrault.ssg.buildscript.BuildSpec;
import com.jessebrault.ssg.text.Text;
import com.jessebrault.ssg.text.TextSupplier;
import java.util.HashSet;
import java.util.Set;
public class DefaultTextsGetter implements TextsGetter {
@Override
public Set<Text> getTexts(BuildSpec buildSpec) {
final Set<TextSupplier> textSuppliers = buildSpec.getTextSuppliers().get(() ->
new SsgException("The textSuppliers Property in " + buildSpec.getName()
+ " must contain at least an empty Set.")
);
final Set<Text> texts = new HashSet<>();
for (final var textSupplier : textSuppliers) {
texts.addAll(textSupplier.get());
}
return texts;
}
}

View File

@ -0,0 +1,22 @@
package com.jessebrault.ssg;
import com.jessebrault.ssg.view.WvcCompiler;
import groovy.lang.GroovyClassLoader;
import groowt.view.component.compiler.SimpleComponentTemplateClassFactory;
import jakarta.inject.Inject;
public class DefaultWvcCompilerFactory implements WvcCompilerFactory {
private final GroovyClassLoader groovyClassLoader;
@Inject
public DefaultWvcCompilerFactory(GroovyClassLoader groovyClassLoader) {
this.groovyClassLoader = groovyClassLoader;
}
@Override
public WvcCompiler getWvcCompiler() {
return new WvcCompiler(this.groovyClassLoader, new SimpleComponentTemplateClassFactory(this.groovyClassLoader));
}
}

View File

@ -0,0 +1,43 @@
package com.jessebrault.ssg;
import com.jessebrault.ssg.page.Page;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.List;
public class FilePageWriter implements PageWriter {
@Override
public void write(Page page, File outputDir, String renderedPage) {
if (!outputDir.mkdirs()) {
throw new RuntimeException("Could not make directories for outputDir " + outputDir);
};
// calculate target path
final List<String> pathParts = Arrays.asList(page.getPath().split("/"));
if (page.getPath().endsWith("/")) {
pathParts.add("index");
}
final String head = pathParts.getFirst();
final List<String> tail = pathParts.size() > 1 ? pathParts.subList(1, pathParts.size()) : List.of();
final Path path = Path.of(head, tail.toArray(String[]::new));
final File outputFile = new File(outputDir, path + page.getFileExtension());
// make dirs and write
if (!outputFile.getParentFile().mkdirs()) {
throw new RuntimeException("Could not make parent directories for " + outputFile);
}
try (final FileWriter writer = new FileWriter(outputFile)) {
writer.write(renderedPage);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}

View File

@ -0,0 +1,140 @@
package com.jessebrault.ssg;
import com.jessebrault.di.BindingUtil;
import com.jessebrault.di.RegistryObjectFactory;
import com.jessebrault.fp.either.Either;
import com.jessebrault.ssg.buildscript.BuildSpec;
import com.jessebrault.ssg.buildscript.BuildSpecFactory;
import com.jessebrault.ssg.di.PagesExtension;
import com.jessebrault.ssg.di.SelfPageExtension;
import com.jessebrault.ssg.page.Page;
import com.jessebrault.ssg.util.Diagnostic;
import groovy.lang.GroovyClassLoader;
import groowt.view.component.web.WebViewComponent;
import io.github.classgraph.ClassGraph;
import io.github.classgraph.ScanResult;
import jakarta.inject.Inject;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
public class JDefaultStaticSiteGenerator implements StaticSiteGenerator {
private final BuildSpecFactory buildSpecFactory;
private final ObjectFactoryConfigurator objectFactoryConfigurator;
private final GroovyClassLoader groovyClassLoader;
private final ExecutorService executorService;
private final PageScanner pageScanner;
private final ComponentClassScanner componentClassScanner;
private final PageRenderer pageRenderer;
private final PageWriter pageWriter;
@Inject
public JDefaultStaticSiteGenerator(
BuildSpecFactory buildSpecFactory,
ObjectFactoryConfigurator objectFactoryConfigurator,
GroovyClassLoader groovyClassLoader,
ExecutorService executorService,
PageScanner pageScanner,
ComponentClassScanner componentClassScanner,
PageRenderer pageRenderer,
PageWriter pageWriter
) {
this.buildSpecFactory = buildSpecFactory;
this.objectFactoryConfigurator = objectFactoryConfigurator;
this.groovyClassLoader = groovyClassLoader;
this.executorService = executorService;
this.pageScanner = pageScanner;
this.componentClassScanner = componentClassScanner;
this.pageRenderer = pageRenderer;
this.pageWriter = pageWriter;
}
@Override
public Collection<Diagnostic> doBuild(String buildScriptFqn, Map<String, String> buildScriptCliArgs) {
// Get build spec
final BuildSpec buildSpec = this.buildSpecFactory.getBuildSpec(buildScriptFqn, buildScriptCliArgs);
// Prepare object factory for rendering pages and components
final RegistryObjectFactory buildObjectFactory = buildSpec.getObjectFactory().get(() ->
new SsgException("objectFactory Provider in " + buildSpec.getName() + " must be set.")
);
this.objectFactoryConfigurator.configure(buildObjectFactory, buildSpec);
// ClassGraph scan of base packages
final Set<String> basePackages = buildSpec.getBasePackages().get(() ->
new SsgException("basePackages Provider in " + buildSpec.getName() + " must be at least an empty Set.")
);
final ClassGraph classGraph = new ClassGraph()
.enableAnnotationInfo()
.addClassLoader(this.groovyClassLoader);
for (final String basePackage : basePackages) {
classGraph.acceptPackages(basePackage);
}
// Get all pages and components from scan
final Set<Page> pages = ConcurrentHashMap.newKeySet();
final Set<Class<? extends WebViewComponent>> componentClasses = ConcurrentHashMap.newKeySet();
try (final ScanResult scanResult = classGraph.scan()) {
// fork
final CompletableFuture<Void> pagesFuture = CompletableFuture.runAsync(
() -> pages.addAll(this.pageScanner.getAllPages(scanResult, buildObjectFactory)),
this.executorService
);
final CompletableFuture<Void> componentsFuture = CompletableFuture.runAsync(
() -> componentClasses.addAll(this.componentClassScanner.getWebViewComponentClasses(scanResult)),
this.executorService
);
// join
CompletableFuture.allOf(pagesFuture, componentsFuture).join();
}
// final ObjectFactory configuration to add all pages/components found AND self page extension
buildObjectFactory.configureRegistry(registry -> {
final var pagesExtension = new PagesExtension();
pagesExtension.getAllPages().addAll(pages);
registry.addExtension(pagesExtension);
registry.bind(BindingUtil.named("allWvc", Set.class), BindingUtil.toSingleton(componentClasses));
registry.addExtension(new SelfPageExtension());
});
// render each page
final Set<Diagnostic> diagnostics = ConcurrentHashMap.newKeySet();
final List<CompletableFuture<Void>> renderAndWriteFutures = pages.stream()
.map(page -> CompletableFuture.runAsync(() -> {
final Either<Diagnostic, String> renderResult = this.pageRenderer.renderPage(
page,
buildSpec.getBaseUrl().get(() ->
new SsgException("baseUrl Provider in " + buildSpec.getName() + " must be set.")
),
buildObjectFactory,
componentClasses
);
if (renderResult.isLeft()) {
diagnostics.add(renderResult.getLeft());
return;
}
this.pageWriter.write(
page,
buildSpec.getOutputDir().get(() ->
new SsgException("outputDir Provider in " + buildSpec.getName() + " must be set.")
),
renderResult.getRight()
);
}, this.executorService))
.toList();
CompletableFuture.allOf(renderAndWriteFutures.toArray(new CompletableFuture[0])).join();
return diagnostics;
}
}

View File

@ -0,0 +1,8 @@
package com.jessebrault.ssg;
import com.jessebrault.di.RegistryObjectFactory;
import com.jessebrault.ssg.buildscript.BuildSpec;
public interface ObjectFactoryConfigurator {
void configure(RegistryObjectFactory registryObjectFactory, BuildSpec buildSpec);
}

View File

@ -0,0 +1,16 @@
package com.jessebrault.ssg;
import com.jessebrault.di.ObjectFactory;
import com.jessebrault.ssg.view.WvcPageView;
import groowt.view.component.web.WebViewComponent;
import groowt.view.component.web.WebViewComponentContext;
import java.util.Set;
public interface PageContextFactory {
WebViewComponentContext makeContext(
WvcPageView wvcPageView,
ObjectFactory buildObjectFactory,
Set<Class<? extends WebViewComponent>> allWvcClasses
);
}

View File

@ -0,0 +1,18 @@
package com.jessebrault.ssg;
import com.jessebrault.di.ObjectFactory;
import com.jessebrault.fp.either.Either;
import com.jessebrault.ssg.page.Page;
import com.jessebrault.ssg.util.Diagnostic;
import groowt.view.component.web.WebViewComponent;
import java.util.Set;
public interface PageRenderer {
Either<Diagnostic, String> renderPage(
Page page,
String baseUrl,
ObjectFactory buildObjectFactory,
Set<Class<? extends WebViewComponent>> allWvcClasses
);
}

View File

@ -0,0 +1,11 @@
package com.jessebrault.ssg;
import com.jessebrault.di.ObjectFactory;
import com.jessebrault.ssg.page.Page;
import io.github.classgraph.ScanResult;
import java.util.Set;
public interface PageScanner {
Set<Page> getAllPages(ScanResult scanResult, ObjectFactory buildObjectFactory);
}

View File

@ -0,0 +1,9 @@
package com.jessebrault.ssg;
import com.jessebrault.ssg.page.Page;
import java.io.File;
public interface PageWriter {
void write(Page page, File outputDir, String renderedPage);
}

View File

@ -1,12 +1,13 @@
package com.jessebrault.ssg package com.jessebrault.ssg;
import com.jessebrault.ssg.util.Diagnostic import com.jessebrault.ssg.util.Diagnostic;
interface StaticSiteGenerator { import java.util.Collection;
import java.util.Map;
public interface StaticSiteGenerator {
Collection<Diagnostic> doBuild( Collection<Diagnostic> doBuild(
File projectDir,
String buildName,
String buildScriptFqn, String buildScriptFqn,
Map<String, String> buildScriptCliArgs Map<String, String> buildScriptCliArgs
) );
} }

View File

@ -0,0 +1,10 @@
package com.jessebrault.ssg;
import com.jessebrault.ssg.buildscript.BuildSpec;
import com.jessebrault.ssg.text.Text;
import java.util.Set;
public interface TextsGetter {
Set<Text> getTexts(BuildSpec buildSpec);
}

View File

@ -0,0 +1,7 @@
package com.jessebrault.ssg;
import com.jessebrault.ssg.view.WvcCompiler;
public interface WvcCompilerFactory {
WvcCompiler getWvcCompiler();
}

View File

@ -0,0 +1,8 @@
package com.jessebrault.ssg;
import com.jessebrault.ssg.view.WvcPageView;
import groowt.view.component.web.WebViewComponentContext;
public interface WvcContextFactory {
WebViewComponentContext getContext(WvcPageView currentPage);
}

View File

@ -1,5 +1,6 @@
package com.jessebrault.ssg.buildscript package com.jessebrault.ssg.buildscript
import com.jessebrault.di.ObjectFactory
import com.jessebrault.ssg.buildscript.delegates.BuildDelegate import com.jessebrault.ssg.buildscript.delegates.BuildDelegate
import org.jetbrains.annotations.ApiStatus import org.jetbrains.annotations.ApiStatus
import org.jetbrains.annotations.Nullable import org.jetbrains.annotations.Nullable
@ -25,6 +26,8 @@ abstract class BuildScriptBase extends Script {
private Closure buildClosure = { } private Closure buildClosure = { }
private File projectRoot private File projectRoot
private String buildName private String buildName
private ObjectFactory objectFactory
private Map<String, String> cliArgs
/* --- Instance DSL helpers --- */ /* --- Instance DSL helpers --- */
@ -73,4 +76,21 @@ abstract class BuildScriptBase extends Script {
this.buildClosure this.buildClosure
} }
ObjectFactory getObjectFactory() {
return objectFactory
}
@ApiStatus.Internal
void setObjectFactory(ObjectFactory objectFactory) {
this.objectFactory = objectFactory
}
Map<String, String> getCliArgs() {
return cliArgs
}
void setCliArgs(Map<String, String> cliArgs) {
this.cliArgs = cliArgs
}
} }

View File

@ -0,0 +1,7 @@
package com.jessebrault.ssg.buildscript;
import java.util.Map;
public interface BuildScriptFactory {
BuildScriptBase getAndRunBuildScript(String scriptFqn, Map<String, String> scriptCliArgs);
}

View File

@ -5,13 +5,12 @@ import groovy.transform.NullCheck
import groovy.transform.TupleConstructor import groovy.transform.TupleConstructor
import java.util.function.Function import java.util.function.Function
import java.util.function.Supplier
@NullCheck @NullCheck
@TupleConstructor(includeFields = true) @TupleConstructor(includeFields = true)
class BuildScriptToBuildSpecConverter { class BuildScriptToBuildSpecConverter {
private final BuildScriptGetter buildScriptGetter private final BuildScriptFactory buildScriptFactory
private final Function<String, BuildDelegate> buildDelegateFactory private final Function<String, BuildDelegate> buildDelegateFactory
protected BuildSpec getFromDelegate(String name, BuildDelegate delegate) { protected BuildSpec getFromDelegate(String name, BuildDelegate delegate) {

View File

@ -1,11 +1,11 @@
package com.jessebrault.ssg.buildscript 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 com.jessebrault.di.RegistryObjectFactory import com.jessebrault.di.RegistryObjectFactory
import com.jessebrault.fp.provider.Provider import com.jessebrault.fp.provider.Provider
import com.jessebrault.ssg.model.Model
import com.jessebrault.ssg.text.TextSupplier
import groovy.transform.EqualsAndHashCode
import groovy.transform.NullCheck
import static com.jessebrault.ssg.util.ObjectUtil.requireProvider import static com.jessebrault.ssg.util.ObjectUtil.requireProvider
import static com.jessebrault.ssg.util.ObjectUtil.requireString import static com.jessebrault.ssg.util.ObjectUtil.requireString
@ -21,9 +21,8 @@ final class BuildSpec {
final Provider<File> outputDir final Provider<File> outputDir
final Provider<Map<String, Object>> globals final Provider<Map<String, Object>> globals
final Provider<Set<Model>> models final Provider<Set<Model>> models
final Provider<Set<File>> textsDirs final Provider<Set<TextSupplier>> textSuppliers
final Provider<Set<TextConverter>> textConverters final Provider<RegistryObjectFactory> objectFactory
final Provider<RegistryObjectFactory.Builder> objectFactoryBuilder
@SuppressWarnings('GroovyAssignabilityCheck') @SuppressWarnings('GroovyAssignabilityCheck')
BuildSpec(Map args) { BuildSpec(Map args) {
@ -34,15 +33,14 @@ final class BuildSpec {
this.outputDir = requireProvider(args.outputDir) this.outputDir = requireProvider(args.outputDir)
this.globals = requireProvider(args.globals) this.globals = requireProvider(args.globals)
this.models = requireProvider(args.models) this.models = requireProvider(args.models)
this.textsDirs = requireProvider(args.textsDirs) this.textSuppliers = requireProvider(args.textSuppliers)
this.textConverters = requireProvider(args.textConverters) this.objectFactory = requireProvider(args.objectFactory)
this.objectFactoryBuilder = requireProvider(args.objectFactoryBuilder)
} }
@Override @Override
String toString() { String toString() {
"Build(name: ${this.name}, basePackages: $basePackages, siteName: $siteName, " + "Build(name: ${this.name}, basePackages: $basePackages, siteName: $siteName, " +
"baseUrl: $baseUrl, outputDir: $outputDir, textsDirs: $textsDirs)" "baseUrl: $baseUrl, outputDir: $outputDir, textSuppliers: $textSuppliers)"
} }
} }

View File

@ -0,0 +1,7 @@
package com.jessebrault.ssg.buildscript;
import java.util.Map;
public interface BuildSpecFactory {
BuildSpec getBuildSpec(String scriptFqn, Map<String, String> scriptCliArgs);
}

View File

@ -0,0 +1,78 @@
package com.jessebrault.ssg.buildscript;
import com.jessebrault.di.ObjectFactory;
import groovy.lang.GroovyClassLoader;
import jakarta.inject.Inject;
import jakarta.inject.Named;
import org.codehaus.groovy.control.CompilerConfiguration;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.List;
import java.util.Map;
public class DefaultBuildScriptFactory implements BuildScriptFactory {
private final GroovyClassLoader groovyClassLoader;
private final List<URL> scriptBaseUrls;
private final File projectDir;
private final ObjectFactory objectFactory;
@Inject
public DefaultBuildScriptFactory(
GroovyClassLoader groovyClassLoader,
@Named("scriptBaseUrls") List<URL> scriptBaseUrls,
@Named("projectDir") File projectDir,
ObjectFactory objectFactory
) {
this.groovyClassLoader = groovyClassLoader;
this.scriptBaseUrls = scriptBaseUrls;
this.projectDir = projectDir;
this.objectFactory = objectFactory;
}
protected GroovyClassLoader getScriptClassLoader() {
// set up gcl with our base script class
final var compilerConfiguration = new CompilerConfiguration();
compilerConfiguration.setScriptBaseClass(BuildScriptBase.class.getName());
final var scriptGroovyClassLoader = new GroovyClassLoader(
this.groovyClassLoader,
compilerConfiguration
);
// add urls where to find scripts
for (final var url : this.scriptBaseUrls) {
scriptGroovyClassLoader.addURL(url);
}
return scriptGroovyClassLoader;
}
@Override
public BuildScriptBase getAndRunBuildScript(String scriptFqn, Map<String, String> scriptCliArgs) {
try (final GroovyClassLoader scriptClassLoader = this.getScriptClassLoader()) {
// Get script instance
@SuppressWarnings("unchecked")
final Class<? extends BuildScriptBase> scriptClass = (Class<? extends BuildScriptBase>)
scriptClassLoader.loadClass(scriptFqn, true, true);
final BuildScriptBase script = scriptClass.getConstructor().newInstance();
// configure props
script.setProjectRoot(this.projectDir);
script.setBuildName(scriptFqn);
script.setObjectFactory(this.objectFactory);
script.setCliArgs(scriptCliArgs);
// run
script.run();
return script;
} catch (IOException | ClassNotFoundException | NoSuchMethodException | InstantiationException |
IllegalAccessException | InvocationTargetException exception) {
throw new RuntimeException(exception);
}
}
}

View File

@ -0,0 +1,64 @@
package com.jessebrault.ssg.buildscript;
import com.jessebrault.ssg.buildscript.delegates.BuildDelegate;
import com.jessebrault.ssg.buildscript.delegates.BuildDelegateConfigurator;
import com.jessebrault.ssg.buildscript.delegates.BuildDelegateConverter;
import jakarta.inject.Inject;
import jakarta.inject.Named;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.util.Deque;
import java.util.LinkedList;
import java.util.Map;
public class DefaultBuildSpecFactory implements BuildSpecFactory {
private final BuildScriptFactory buildScriptFactory;
private final BuildDelegateConfigurator buildDelegateConfigurator;
private final BuildDelegateConverter buildDelegateConverter;
private final File projectDir;
@Inject
public DefaultBuildSpecFactory(
BuildScriptFactory buildScriptFactory,
BuildDelegateConfigurator buildDelegateConfigurator,
BuildDelegateConverter buildDelegateConverter,
@Named("projectDir") File projectDir
) {
this.buildScriptFactory = buildScriptFactory;
this.buildDelegateConfigurator = buildDelegateConfigurator;
this.buildDelegateConverter = buildDelegateConverter;
this.projectDir = projectDir;
}
protected BuildSpec doConvert(String scriptFqn, Map<String, String> scriptCliArgs, BuildScriptBase script) {
// 1. Make hierarchy as a stack
final Deque<BuildScriptBase> buildHierarchy = new LinkedList<>();
buildHierarchy.push(script);
@Nullable String extending = script.getExtending();
while (extending != null) {
final BuildScriptBase from = this.buildScriptFactory.getAndRunBuildScript(extending, scriptCliArgs);
buildHierarchy.push(from);
extending = from.getExtending();
}
// Go through the stack from top to bottom, using the same delegate
final BuildDelegate buildDelegate = new BuildDelegate(this.projectDir);
this.buildDelegateConfigurator.configure(buildDelegate, scriptFqn);
while (!buildHierarchy.isEmpty()) {
final BuildScriptBase from = buildHierarchy.pop();
from.getBuildClosure().setDelegate(buildDelegate);
from.getBuildClosure().run();
}
return this.buildDelegateConverter.convert(scriptFqn, buildDelegate);
}
@Override
public BuildSpec getBuildSpec(String scriptFqn, Map<String, String> scriptCliArgs) {
final BuildScriptBase script = this.buildScriptFactory.getAndRunBuildScript(scriptFqn, scriptCliArgs);
return this.doConvert(scriptFqn, scriptCliArgs, script);
}
}

View File

@ -1,35 +1,18 @@
package com.jessebrault.ssg.buildscript.delegates package com.jessebrault.ssg.buildscript.delegates
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 com.jessebrault.ssg.util.PathUtil
import com.jessebrault.di.DefaultRegistryObjectFactory
import com.jessebrault.di.RegistryObjectFactory import com.jessebrault.di.RegistryObjectFactory
import com.jessebrault.fp.property.DefaultProperty import com.jessebrault.fp.property.DefaultProperty
import com.jessebrault.fp.property.Property import com.jessebrault.fp.property.Property
import com.jessebrault.fp.provider.DefaultProvider
import com.jessebrault.fp.provider.NamedProvider import com.jessebrault.fp.provider.NamedProvider
import com.jessebrault.fp.provider.Provider import com.jessebrault.fp.provider.Provider
import com.jessebrault.ssg.model.Model
import com.jessebrault.ssg.model.Models
import com.jessebrault.ssg.text.TextSupplier
import java.nio.file.Path
import java.util.function.Supplier import java.util.function.Supplier
final class BuildDelegate { final class BuildDelegate {
static BuildDelegate withDefaults(String buildName, File projectDir) {
new BuildDelegate(projectDir).tap {
basePackages.convention = [] as Set<String>
outputDir.convention = PathUtil.resolve(projectDir, Path.of('dist', buildName.split(/\\./)))
globals.convention = [:]
models.convention = [] as Set<Model>
textsDirs.convention = [new File(projectDir, 'texts')] as Set<File>
textConverters.convention = [new MarkdownTextConverter()] as Set<TextConverter>
objectFactoryBuilder.convention = DefaultRegistryObjectFactory.Builder.withDefaults()
}
}
final File projectDir final File projectDir
final Property<Set<String>> basePackages = DefaultProperty.<Set<String>>empty(Set) final Property<Set<String>> basePackages = DefaultProperty.<Set<String>>empty(Set)
@ -38,12 +21,10 @@ final class BuildDelegate {
final Property<File> outputDir = DefaultProperty.empty(File) final Property<File> outputDir = DefaultProperty.empty(File)
final Property<Map<String, Object>> globals = DefaultProperty.<Map<String, Object>>empty(Map) final Property<Map<String, Object>> globals = DefaultProperty.<Map<String, Object>>empty(Map)
final Property<Set<Model>> models = DefaultProperty.<Set<Model>>empty(Set) final Property<Set<Model>> models = DefaultProperty.<Set<Model>>empty(Set)
final Property<Set<File>> textsDirs = DefaultProperty.<Set<File>>empty(Set) final Property<Set<TextSupplier>> textSuppliers = DefaultProperty.<Set<TextSupplier>>empty(Set)
final Property<Set<TextConverter>> textConverters = DefaultProperty.<Set<TextConverter>>empty(Set) final Property<RegistryObjectFactory> objectFactory = DefaultProperty.empty(RegistryObjectFactory)
final Property<RegistryObjectFactory.Builder> objectFactoryBuilder =
DefaultProperty.empty(RegistryObjectFactory.Builder)
private BuildDelegate(File projectDir) { BuildDelegate(File projectDir) {
this.projectDir = projectDir this.projectDir = projectDir
} }
@ -109,24 +90,12 @@ final class BuildDelegate {
this.models.configure { it.add(Models.ofNamedProvider(namedProvider)) } this.models.configure { it.add(Models.ofNamedProvider(namedProvider)) }
} }
void textsDir(File textsDir) { void textSupplier(TextSupplier toAdd) {
this.textsDirs.configure { it.add(textsDir) } this.textSuppliers.configure { it.add(toAdd) }
} }
void textsDirs(File... textsDirs) { void textSuppliers(TextSupplier... toAdd) {
textsDirs.each { this.textsDir(it) } this.textSuppliers.configure { it.addAll(toAdd) }
}
void textConverter(TextConverter textConverter) {
this.textConverters.configure { it.add(textConverter) }
}
void textConverters(TextConverter... textConverters) {
textConverters.each { this.textConverter(it) }
}
void objectFactoryBuilder(RegistryObjectFactory.Builder builder) {
this.objectFactoryBuilder.set(builder)
} }
} }

View File

@ -0,0 +1,5 @@
package com.jessebrault.ssg.buildscript.delegates;
public interface BuildDelegateConfigurator {
void configure(BuildDelegate buildDelegate, String buildName);
}

View File

@ -0,0 +1,7 @@
package com.jessebrault.ssg.buildscript.delegates;
import com.jessebrault.ssg.buildscript.BuildSpec;
public interface BuildDelegateConverter {
BuildSpec convert(String buildName, BuildDelegate buildDelegate);
}

View File

@ -0,0 +1,39 @@
package com.jessebrault.ssg.buildscript.delegates
import com.jessebrault.di.DefaultRegistryObjectFactory
import com.jessebrault.ssg.model.Model
import com.jessebrault.ssg.text.TextSupplier
import com.jessebrault.ssg.text.TextsDirMarkdownTextSupplier
import com.jessebrault.ssg.util.PathUtil
import jakarta.inject.Inject
import jakarta.inject.Named
import java.nio.file.Path
class DefaultBuildDelegateConfigurator implements BuildDelegateConfigurator {
private final File projectDir
private final TextsDirMarkdownTextSupplier textsDirMarkdownTextSupplier
@Inject
DefaultBuildDelegateConfigurator(
@Named('projectDir') File projectDir,
TextsDirMarkdownTextSupplier textsDirMarkdownTextSupplier
) {
this.projectDir = projectDir
this.textsDirMarkdownTextSupplier = textsDirMarkdownTextSupplier
}
@Override
void configure(BuildDelegate buildDelegate, String buildName) {
buildDelegate.tap {
basePackages.convention = [] as Set<String>
outputDir.convention = PathUtil.resolve(this.projectDir, Path.of('dist', buildName.split(/\\./)))
globals.convention = [:]
models.convention = [] as Set<Model>
textSuppliers.convention = [this.textsDirMarkdownTextSupplier] as Set<TextSupplier>
objectFactory.convention = DefaultRegistryObjectFactory.Builder.withDefaults().build()
}
}
}

View File

@ -0,0 +1,22 @@
package com.jessebrault.ssg.buildscript.delegates
import com.jessebrault.ssg.buildscript.BuildSpec
class DefaultBuildDelegateConverter implements BuildDelegateConverter {
@Override
BuildSpec convert(String buildName, BuildDelegate delegate) {
return new BuildSpec(
name: buildName,
basePackages: delegate.basePackages,
siteName: delegate.siteName,
baseUrl: delegate.baseUrl,
outputDir: delegate.outputDir,
globals: delegate.globals,
models: delegate.models,
textSuppliers: delegate.textSuppliers,
objectFactory: delegate.objectFactory
)
}
}

View File

@ -0,0 +1,8 @@
package com.jessebrault.ssg.text;
import java.util.Collection;
@FunctionalInterface
public interface TextSupplier {
Collection<Text> get();
}

View File

@ -0,0 +1,58 @@
package com.jessebrault.ssg.text;
import jakarta.inject.Inject;
import jakarta.inject.Named;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.stream.Stream;
public class TextsDirMarkdownTextSupplier implements TextSupplier {
private final File projectDir;
private final ExecutorService executorService;
private final MarkdownTextConverter markdownTextConverter;
@Inject
public TextsDirMarkdownTextSupplier(
@Named("projectDir") File projectDir,
ExecutorService executorService,
MarkdownTextConverter markdownTextConverter
) {
this.projectDir = projectDir;
this.executorService = executorService;
this.markdownTextConverter = markdownTextConverter;
}
@Override
public Collection<Text> get() {
final Path textsDir = Paths.get(projectDir.getAbsolutePath(), "texts");
final Collection<Text> results = ConcurrentHashMap.newKeySet();
if (Files.exists(textsDir)) {
try (final Stream<Path> walkStream = Files.walk(textsDir)){
final List<CompletableFuture<Void>> textFutures = walkStream
.map(path -> {
return CompletableFuture.runAsync(() -> {
if (path.endsWith(".md")) {
results.add(this.markdownTextConverter.convert(textsDir.toFile(), path.toFile()));
}
}, this.executorService);
})
.toList();
CompletableFuture.allOf(textFutures.toArray(new CompletableFuture[0])).join();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
return results;
}
}

View File

@ -4,13 +4,17 @@ import groovy.transform.EqualsAndHashCode
import groovy.transform.TupleConstructor import groovy.transform.TupleConstructor
import org.jetbrains.annotations.Nullable import org.jetbrains.annotations.Nullable
@TupleConstructor
@EqualsAndHashCode @EqualsAndHashCode
final class Diagnostic { final class Diagnostic {
final String message final String message
final @Nullable Exception exception final @Nullable Exception exception
Diagnostic(String message, Exception exception) {
this.message = message
this.exception = exception
}
@Override @Override
String toString() { String toString() {
if (this.exception != null) { if (this.exception != null) {

View File

@ -1,15 +1,13 @@
package com.jessebrault.ssg.view package com.jessebrault.ssg.view
import com.jessebrault.ssg.util.Diagnostic
import groovy.transform.TupleConstructor
import com.jessebrault.fp.either.Either import com.jessebrault.fp.either.Either
import com.jessebrault.ssg.util.Diagnostic
import groowt.view.component.ComponentTemplate import groowt.view.component.ComponentTemplate
import groowt.view.component.ViewComponent import groowt.view.component.ViewComponent
import groowt.view.component.compiler.ComponentTemplateClassFactory import groowt.view.component.compiler.ComponentTemplateClassFactory
import groowt.view.component.compiler.source.ComponentTemplateSource import groowt.view.component.compiler.source.ComponentTemplateSource
import groowt.view.component.web.compiler.DefaultWebViewComponentTemplateCompileUnit import groowt.view.component.web.compiler.DefaultWebViewComponentTemplateCompileUnit
@TupleConstructor
class WvcCompiler { class WvcCompiler {
private static class SsgWvcTemplateCompileUnit extends DefaultWebViewComponentTemplateCompileUnit { private static class SsgWvcTemplateCompileUnit extends DefaultWebViewComponentTemplateCompileUnit {
@ -30,6 +28,11 @@ class WvcCompiler {
final GroovyClassLoader groovyClassLoader final GroovyClassLoader groovyClassLoader
final ComponentTemplateClassFactory templateClassFactory final ComponentTemplateClassFactory templateClassFactory
WvcCompiler(GroovyClassLoader groovyClassLoader, ComponentTemplateClassFactory templateClassFactory) {
this.groovyClassLoader = groovyClassLoader
this.templateClassFactory = templateClassFactory
}
Either<Diagnostic, ComponentTemplate> compileTemplate( Either<Diagnostic, ComponentTemplate> compileTemplate(
Class<? extends ViewComponent> componentClass, Class<? extends ViewComponent> componentClass,
String resourceName String resourceName
@ -37,7 +40,8 @@ class WvcCompiler {
def templateUrl = componentClass.getResource(resourceName) def templateUrl = componentClass.getResource(resourceName)
if (templateUrl == null) { if (templateUrl == null) {
return Either.left(new Diagnostic( return Either.left(new Diagnostic(
"Could not find templateResource: $resourceName" "Could not find templateResource: $resourceName",
null
)) ))
} }
def source = ComponentTemplateSource.of(templateUrl) def source = ComponentTemplateSource.of(templateUrl)

View File

@ -4,7 +4,7 @@ plugins {
} }
group 'com.jessebrault.ssg' group 'com.jessebrault.ssg'
version '0.6.3' version '0.7.0-SNAPSHOT'
repositories { repositories {
mavenCentral() mavenCentral()

View File

@ -1,8 +1,20 @@
package com.jessebrault.ssg package com.jessebrault.ssg
import com.jessebrault.di.DefaultRegistryObjectFactory
import com.jessebrault.di.ObjectFactory
import com.jessebrault.di.RegistryObjectFactory
import com.jessebrault.ssg.buildscript.BuildScriptFactory
import com.jessebrault.ssg.buildscript.BuildSpecFactory
import com.jessebrault.ssg.buildscript.DefaultBuildScriptFactory
import com.jessebrault.ssg.buildscript.DefaultBuildSpecFactory
import com.jessebrault.ssg.buildscript.delegates.BuildDelegateConfigurator
import com.jessebrault.ssg.buildscript.delegates.BuildDelegateConverter
import com.jessebrault.ssg.buildscript.delegates.DefaultBuildDelegateConfigurator
import com.jessebrault.ssg.buildscript.delegates.DefaultBuildDelegateConverter
import com.jessebrault.ssg.gradle.SsgBuildModel import com.jessebrault.ssg.gradle.SsgBuildModel
import com.jessebrault.ssg.util.Diagnostic import com.jessebrault.ssg.util.Diagnostic
import com.jessebrault.ssg.util.URLUtil import com.jessebrault.ssg.util.URLUtil
import com.jessebrault.ssg.view.WvcCompiler
import org.apache.logging.log4j.LogManager import org.apache.logging.log4j.LogManager
import org.apache.logging.log4j.Logger import org.apache.logging.log4j.Logger
import org.gradle.tooling.GradleConnector import org.gradle.tooling.GradleConnector
@ -10,6 +22,10 @@ import picocli.CommandLine
import java.nio.file.Files import java.nio.file.Files
import java.nio.file.Path import java.nio.file.Path
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors
import static com.jessebrault.di.BindingUtil.*
abstract class AbstractBuildCommand extends AbstractSubCommand { abstract class AbstractBuildCommand extends AbstractSubCommand {
@ -74,8 +90,47 @@ abstract class AbstractBuildCommand extends AbstractSubCommand {
) )
boolean profile boolean profile
@CommandLine.Option(
names = ['-t', '--threads'],
description = 'The number of threads to use.',
defaultValue = '8'
)
Integer threads
protected RegistryObjectFactory objectFactory = null
protected StaticSiteGenerator staticSiteGenerator = null protected StaticSiteGenerator staticSiteGenerator = null
protected RegistryObjectFactory getApiObjectFactory(
GroovyClassLoader groovyClassLoader,
ExecutorService executorService,
PageWriter pageWriter,
List<URL> scriptBaseUrls,
File projectDir
) {
RegistryObjectFactory objectFactory = DefaultRegistryObjectFactory.Builder.withDefaults().build()
objectFactory.tap {
configureRegistry {
bind(BuildSpecFactory, toClass(DefaultBuildSpecFactory))
bind(ObjectFactoryConfigurator, toClass(DefaultObjectFactoryConfigurator))
bind(GroovyClassLoader, toSingleton(groovyClassLoader))
bind(ExecutorService, toSingleton(executorService))
bind(PageScanner, toClass(DefaultPageScanner))
bind(ComponentClassScanner, toClass(DefaultComponentClassScanner))
bind(PageRenderer, toClass(DefaultPageRenderer))
bind(PageWriter, toSingleton(pageWriter))
bind(BuildScriptFactory, toClass(DefaultBuildScriptFactory))
bind(BuildDelegateConfigurator, toClass(DefaultBuildDelegateConfigurator))
bind(BuildDelegateConverter, toClass(DefaultBuildDelegateConverter))
bind(named('scriptBaseUrls', List), toSingleton(scriptBaseUrls))
bind(named('projectDir', File), toSingleton(projectDir))
bind(TextsGetter, toClass(DefaultTextsGetter))
bind(WvcCompiler, toSelf())
bind(PageContextFactory, toClass(DefaultPageContextFactory))
bind(ObjectFactory, toSingleton(objectFactory))
}
}
}
protected final Integer doSingleBuild(String buildName) { protected final Integer doSingleBuild(String buildName) {
logger.traceEntry('buildName: {}', buildName) logger.traceEntry('buildName: {}', buildName)
@ -126,23 +181,21 @@ abstract class AbstractBuildCommand extends AbstractSubCommand {
def buildScriptDirUrls = this.buildScriptDirs.collect { def buildScriptDirUrls = this.buildScriptDirs.collect {
def withProjectDir = new File(this.commonCliOptions.projectDir, it.toString()) def withProjectDir = new File(this.commonCliOptions.projectDir, it.toString())
withProjectDir.toURI().toURL() withProjectDir.toURI().toURL()
} as URL[] }
this.staticSiteGenerator = new DefaultStaticSiteGenerator( this.objectFactory = this.getApiObjectFactory(
groovyClassLoader, groovyClassLoader,
Executors.newFixedThreadPool(this.threads),
!this.dryRun ? new FilePageWriter() : new ConsolePageWriter(),
buildScriptDirUrls, buildScriptDirUrls,
this.dryRun new File('.')
) )
this.staticSiteGenerator = objectFactory.createInstance(JDefaultStaticSiteGenerator)
} }
def buildStartTime = System.currentTimeMillis() def buildStartTime = System.currentTimeMillis()
final Collection<Diagnostic> diagnostics = this.staticSiteGenerator.doBuild( final Collection<Diagnostic> diagnostics = this.staticSiteGenerator.doBuild(buildName, this.scriptArgs ?: [:])
this.commonCliOptions.projectDir,
buildName,
buildName,
this.scriptArgs ?: [:]
)
def buildElapsedTime = System.currentTimeMillis() - buildStartTime def buildElapsedTime = System.currentTimeMillis() - buildStartTime
if (this.profile) { if (this.profile) {

View File

@ -5,7 +5,7 @@ import picocli.CommandLine
@CommandLine.Command( @CommandLine.Command(
name = 'ssg', name = 'ssg',
mixinStandardHelpOptions = true, mixinStandardHelpOptions = true,
version = '0.6.3', version = '0.7.0-SNAPSHOT',
description = 'A static site generator which can interface with Gradle for high extensibility.', description = 'A static site generator which can interface with Gradle for high extensibility.',
subcommands = [SsgInit, SsgBuild, SsgWatch] subcommands = [SsgInit, SsgBuild, SsgWatch]
) )

View File

@ -1,5 +1,5 @@
[versions] [versions]
classgraph = '4.8.179' classgraph = '4.8.184'
commonmark = '0.24.0' commonmark = '0.24.0'
di = '0.1.0' di = '0.1.0'
fp = '0.1.0' fp = '0.1.0'

View File

@ -162,8 +162,8 @@ public class SsgGradlePlugin implements Plugin<Project> {
Configuration ssgApiConfiguration, Configuration ssgApiConfiguration,
Configuration ssgCliConfiguration Configuration ssgCliConfiguration
) { ) {
final Dependency ssgApi = project.getDependencies().create("com.jessebrault.ssg:api:0.6.3"); final Dependency ssgApi = project.getDependencies().create("com.jessebrault.ssg:api:0.7.0-SNAPSHOT");
final Dependency ssgCli = project.getDependencies().create("com.jessebrault.ssg:cli:0.6.3"); final Dependency ssgCli = project.getDependencies().create("com.jessebrault.ssg:cli:0.7.0-SNAPSHOT");
ssgApiConfiguration.getDependencies().add(ssgApi); ssgApiConfiguration.getDependencies().add(ssgApi);
ssgCliConfiguration.getDependencies().add(ssgCli); ssgCliConfiguration.getDependencies().add(ssgCli);
} }