initial commit
This commit is contained in:
		
						commit
						30861b3d44
					
				
							
								
								
									
										9
									
								
								.gitattributes
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								.gitattributes
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,9 @@ | ||||
| # | ||||
| # https://help.github.com/articles/dealing-with-line-endings/ | ||||
| # | ||||
| # Linux start script should use lf | ||||
| /gradlew        text eol=lf | ||||
| 
 | ||||
| # These are Windows script files and should use crlf | ||||
| *.bat           text eol=crlf | ||||
| 
 | ||||
							
								
								
									
										7
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,7 @@ | ||||
| # Ignore Gradle project-specific cache directory | ||||
| .gradle | ||||
| 
 | ||||
| # Ignore Gradle build output directory | ||||
| build | ||||
| 
 | ||||
| .idea | ||||
							
								
								
									
										28
									
								
								cli/build.gradle
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								cli/build.gradle
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,28 @@ | ||||
| plugins { | ||||
|     id 'groovy' | ||||
|     id 'application' | ||||
| } | ||||
| 
 | ||||
| repositories { | ||||
|     mavenCentral() | ||||
| } | ||||
| 
 | ||||
| dependencies { | ||||
|     implementation project(':lib') | ||||
| 
 | ||||
|     // https://mvnrepository.com/artifact/org.apache.groovy/groovy | ||||
|     implementation 'org.apache.groovy:groovy:4.0.7' | ||||
| 
 | ||||
|     /** | ||||
|      * Logging | ||||
|      */ | ||||
|     // https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-slf4j-impl | ||||
|     runtimeOnly 'org.apache.logging.log4j:log4j-slf4j-impl:2.19.0' | ||||
| 
 | ||||
|     // https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-core | ||||
|     runtimeOnly 'org.apache.logging.log4j:log4j-core:2.19.0' | ||||
| } | ||||
| 
 | ||||
| application { | ||||
|     mainClassName = 'com.jessebrault.ssg.StaticSiteGeneratorCli' | ||||
| } | ||||
| @ -0,0 +1,30 @@ | ||||
| package com.jessebrault.ssg | ||||
| 
 | ||||
| import com.jessebrault.ssg.frontmatter.MarkdownFrontMatterGetter | ||||
| import com.jessebrault.ssg.renderer.GspRenderer | ||||
| import com.jessebrault.ssg.template.TemplatesFactoryImpl | ||||
| import com.jessebrault.ssg.textfile.TextFilesFactoryImpl | ||||
| import com.jessebrault.ssg.textrenderer.MarkdownRenderer | ||||
| 
 | ||||
| class StaticSiteGeneratorCli { | ||||
| 
 | ||||
|     static void main(String[] args) { | ||||
|         def config = new Config( | ||||
|                 textFilesFactory: new TextFilesFactoryImpl(), | ||||
|                 templatesFactory: new TemplatesFactoryImpl(), | ||||
|                 markdownFrontMatterGetter: new MarkdownFrontMatterGetter(), | ||||
|                 markdownRenderer: new MarkdownRenderer(), | ||||
|                 gspRenderer: new GspRenderer() | ||||
|         ) | ||||
|         def ssg = new StaticSiteGeneratorImpl(config) | ||||
|         def spec = new SiteSpec( | ||||
|                 buildDir: new File('build'), | ||||
|                 staticDir: new File('static'), | ||||
|                 textsDir: new File('texts'), | ||||
|                 templatesDir: new File('templates'), | ||||
|                 templatePartsDir: new File('templatePartsDir') | ||||
|         ) | ||||
|         ssg.generate(spec) | ||||
|     } | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										
											BIN
										
									
								
								gradle/wrapper/gradle-wrapper.jar
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								gradle/wrapper/gradle-wrapper.jar
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										6
									
								
								gradle/wrapper/gradle-wrapper.properties
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								gradle/wrapper/gradle-wrapper.properties
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,6 @@ | ||||
| distributionBase=GRADLE_USER_HOME | ||||
| distributionPath=wrapper/dists | ||||
| distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-bin.zip | ||||
| networkTimeout=10000 | ||||
| zipStoreBase=GRADLE_USER_HOME | ||||
| zipStorePath=wrapper/dists | ||||
							
								
								
									
										244
									
								
								gradlew
									
									
									
									
										vendored
									
									
										Executable file
									
								
							
							
						
						
									
										244
									
								
								gradlew
									
									
									
									
										vendored
									
									
										Executable file
									
								
							| @ -0,0 +1,244 @@ | ||||
| #!/bin/sh | ||||
| 
 | ||||
| # | ||||
| # Copyright © 2015-2021 the original authors. | ||||
| # | ||||
| # Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| # you may not use this file except in compliance with the License. | ||||
| # You may obtain a copy of the License at | ||||
| # | ||||
| #      https://www.apache.org/licenses/LICENSE-2.0 | ||||
| # | ||||
| # Unless required by applicable law or agreed to in writing, software | ||||
| # distributed under the License is distributed on an "AS IS" BASIS, | ||||
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
| # See the License for the specific language governing permissions and | ||||
| # limitations under the License. | ||||
| # | ||||
| 
 | ||||
| ############################################################################## | ||||
| # | ||||
| #   Gradle start up script for POSIX generated by Gradle. | ||||
| # | ||||
| #   Important for running: | ||||
| # | ||||
| #   (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is | ||||
| #       noncompliant, but you have some other compliant shell such as ksh or | ||||
| #       bash, then to run this script, type that shell name before the whole | ||||
| #       command line, like: | ||||
| # | ||||
| #           ksh Gradle | ||||
| # | ||||
| #       Busybox and similar reduced shells will NOT work, because this script | ||||
| #       requires all of these POSIX shell features: | ||||
| #         * functions; | ||||
| #         * expansions «$var», «${var}», «${var:-default}», «${var+SET}», | ||||
| #           «${var#prefix}», «${var%suffix}», and «$( cmd )»; | ||||
| #         * compound commands having a testable exit status, especially «case»; | ||||
| #         * various built-in commands including «command», «set», and «ulimit». | ||||
| # | ||||
| #   Important for patching: | ||||
| # | ||||
| #   (2) This script targets any POSIX shell, so it avoids extensions provided | ||||
| #       by Bash, Ksh, etc; in particular arrays are avoided. | ||||
| # | ||||
| #       The "traditional" practice of packing multiple parameters into a | ||||
| #       space-separated string is a well documented source of bugs and security | ||||
| #       problems, so this is (mostly) avoided, by progressively accumulating | ||||
| #       options in "$@", and eventually passing that to Java. | ||||
| # | ||||
| #       Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, | ||||
| #       and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; | ||||
| #       see the in-line comments for details. | ||||
| # | ||||
| #       There are tweaks for specific operating systems such as AIX, CygWin, | ||||
| #       Darwin, MinGW, and NonStop. | ||||
| # | ||||
| #   (3) This script is generated from the Groovy template | ||||
| #       https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt | ||||
| #       within the Gradle project. | ||||
| # | ||||
| #       You can find Gradle at https://github.com/gradle/gradle/. | ||||
| # | ||||
| ############################################################################## | ||||
| 
 | ||||
| # Attempt to set APP_HOME | ||||
| 
 | ||||
| # Resolve links: $0 may be a link | ||||
| app_path=$0 | ||||
| 
 | ||||
| # Need this for daisy-chained symlinks. | ||||
| while | ||||
|     APP_HOME=${app_path%"${app_path##*/}"}  # leaves a trailing /; empty if no leading path | ||||
|     [ -h "$app_path" ] | ||||
| do | ||||
|     ls=$( ls -ld "$app_path" ) | ||||
|     link=${ls#*' -> '} | ||||
|     case $link in             #( | ||||
|       /*)   app_path=$link ;; #( | ||||
|       *)    app_path=$APP_HOME$link ;; | ||||
|     esac | ||||
| done | ||||
| 
 | ||||
| # This is normally unused | ||||
| # shellcheck disable=SC2034 | ||||
| APP_BASE_NAME=${0##*/} | ||||
| APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit | ||||
| 
 | ||||
| # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. | ||||
| DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' | ||||
| 
 | ||||
| # Use the maximum available, or set MAX_FD != -1 to use that value. | ||||
| MAX_FD=maximum | ||||
| 
 | ||||
| warn () { | ||||
|     echo "$*" | ||||
| } >&2 | ||||
| 
 | ||||
| die () { | ||||
|     echo | ||||
|     echo "$*" | ||||
|     echo | ||||
|     exit 1 | ||||
| } >&2 | ||||
| 
 | ||||
| # OS specific support (must be 'true' or 'false'). | ||||
| cygwin=false | ||||
| msys=false | ||||
| darwin=false | ||||
| nonstop=false | ||||
| case "$( uname )" in                #( | ||||
|   CYGWIN* )         cygwin=true  ;; #( | ||||
|   Darwin* )         darwin=true  ;; #( | ||||
|   MSYS* | MINGW* )  msys=true    ;; #( | ||||
|   NONSTOP* )        nonstop=true ;; | ||||
| esac | ||||
| 
 | ||||
| CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar | ||||
| 
 | ||||
| 
 | ||||
| # Determine the Java command to use to start the JVM. | ||||
| if [ -n "$JAVA_HOME" ] ; then | ||||
|     if [ -x "$JAVA_HOME/jre/sh/java" ] ; then | ||||
|         # IBM's JDK on AIX uses strange locations for the executables | ||||
|         JAVACMD=$JAVA_HOME/jre/sh/java | ||||
|     else | ||||
|         JAVACMD=$JAVA_HOME/bin/java | ||||
|     fi | ||||
|     if [ ! -x "$JAVACMD" ] ; then | ||||
|         die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME | ||||
| 
 | ||||
| Please set the JAVA_HOME variable in your environment to match the | ||||
| location of your Java installation." | ||||
|     fi | ||||
| else | ||||
|     JAVACMD=java | ||||
|     which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. | ||||
| 
 | ||||
| Please set the JAVA_HOME variable in your environment to match the | ||||
| location of your Java installation." | ||||
| fi | ||||
| 
 | ||||
| # Increase the maximum file descriptors if we can. | ||||
| if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then | ||||
|     case $MAX_FD in #( | ||||
|       max*) | ||||
|         # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. | ||||
|         # shellcheck disable=SC3045  | ||||
|         MAX_FD=$( ulimit -H -n ) || | ||||
|             warn "Could not query maximum file descriptor limit" | ||||
|     esac | ||||
|     case $MAX_FD in  #( | ||||
|       '' | soft) :;; #( | ||||
|       *) | ||||
|         # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. | ||||
|         # shellcheck disable=SC3045  | ||||
|         ulimit -n "$MAX_FD" || | ||||
|             warn "Could not set maximum file descriptor limit to $MAX_FD" | ||||
|     esac | ||||
| fi | ||||
| 
 | ||||
| # Collect all arguments for the java command, stacking in reverse order: | ||||
| #   * args from the command line | ||||
| #   * the main class name | ||||
| #   * -classpath | ||||
| #   * -D...appname settings | ||||
| #   * --module-path (only if needed) | ||||
| #   * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. | ||||
| 
 | ||||
| # For Cygwin or MSYS, switch paths to Windows format before running java | ||||
| if "$cygwin" || "$msys" ; then | ||||
|     APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) | ||||
|     CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) | ||||
| 
 | ||||
|     JAVACMD=$( cygpath --unix "$JAVACMD" ) | ||||
| 
 | ||||
|     # Now convert the arguments - kludge to limit ourselves to /bin/sh | ||||
|     for arg do | ||||
|         if | ||||
|             case $arg in                                #( | ||||
|               -*)   false ;;                            # don't mess with options #( | ||||
|               /?*)  t=${arg#/} t=/${t%%/*}              # looks like a POSIX filepath | ||||
|                     [ -e "$t" ] ;;                      #( | ||||
|               *)    false ;; | ||||
|             esac | ||||
|         then | ||||
|             arg=$( cygpath --path --ignore --mixed "$arg" ) | ||||
|         fi | ||||
|         # Roll the args list around exactly as many times as the number of | ||||
|         # args, so each arg winds up back in the position where it started, but | ||||
|         # possibly modified. | ||||
|         # | ||||
|         # NB: a `for` loop captures its iteration list before it begins, so | ||||
|         # changing the positional parameters here affects neither the number of | ||||
|         # iterations, nor the values presented in `arg`. | ||||
|         shift                   # remove old arg | ||||
|         set -- "$@" "$arg"      # push replacement arg | ||||
|     done | ||||
| fi | ||||
| 
 | ||||
| # Collect all arguments for the java command; | ||||
| #   * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of | ||||
| #     shell script including quotes and variable substitutions, so put them in | ||||
| #     double quotes to make sure that they get re-expanded; and | ||||
| #   * put everything else in single quotes, so that it's not re-expanded. | ||||
| 
 | ||||
| set -- \ | ||||
|         "-Dorg.gradle.appname=$APP_BASE_NAME" \ | ||||
|         -classpath "$CLASSPATH" \ | ||||
|         org.gradle.wrapper.GradleWrapperMain \ | ||||
|         "$@" | ||||
| 
 | ||||
| # Stop when "xargs" is not available. | ||||
| if ! command -v xargs >/dev/null 2>&1 | ||||
| then | ||||
|     die "xargs is not available" | ||||
| fi | ||||
| 
 | ||||
| # Use "xargs" to parse quoted args. | ||||
| # | ||||
| # With -n1 it outputs one arg per line, with the quotes and backslashes removed. | ||||
| # | ||||
| # In Bash we could simply go: | ||||
| # | ||||
| #   readarray ARGS < <( xargs -n1 <<<"$var" ) && | ||||
| #   set -- "${ARGS[@]}" "$@" | ||||
| # | ||||
| # but POSIX shell has neither arrays nor command substitution, so instead we | ||||
| # post-process each arg (as a line of input to sed) to backslash-escape any | ||||
| # character that might be a shell metacharacter, then use eval to reverse | ||||
| # that process (while maintaining the separation between arguments), and wrap | ||||
| # the whole thing up as a single "set" statement. | ||||
| # | ||||
| # This will of course break if any of these variables contains a newline or | ||||
| # an unmatched quote. | ||||
| # | ||||
| 
 | ||||
| eval "set -- $( | ||||
|         printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | | ||||
|         xargs -n1 | | ||||
|         sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | | ||||
|         tr '\n' ' ' | ||||
|     )" '"$@"' | ||||
| 
 | ||||
| exec "$JAVACMD" "$@" | ||||
							
								
								
									
										92
									
								
								gradlew.bat
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										92
									
								
								gradlew.bat
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,92 @@ | ||||
| @rem | ||||
| @rem Copyright 2015 the original author or authors. | ||||
| @rem | ||||
| @rem Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| @rem you may not use this file except in compliance with the License. | ||||
| @rem You may obtain a copy of the License at | ||||
| @rem | ||||
| @rem      https://www.apache.org/licenses/LICENSE-2.0 | ||||
| @rem | ||||
| @rem Unless required by applicable law or agreed to in writing, software | ||||
| @rem distributed under the License is distributed on an "AS IS" BASIS, | ||||
| @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
| @rem See the License for the specific language governing permissions and | ||||
| @rem limitations under the License. | ||||
| @rem | ||||
| 
 | ||||
| @if "%DEBUG%"=="" @echo off | ||||
| @rem ########################################################################## | ||||
| @rem | ||||
| @rem  Gradle startup script for Windows | ||||
| @rem | ||||
| @rem ########################################################################## | ||||
| 
 | ||||
| @rem Set local scope for the variables with windows NT shell | ||||
| if "%OS%"=="Windows_NT" setlocal | ||||
| 
 | ||||
| set DIRNAME=%~dp0 | ||||
| if "%DIRNAME%"=="" set DIRNAME=. | ||||
| @rem This is normally unused | ||||
| set APP_BASE_NAME=%~n0 | ||||
| set APP_HOME=%DIRNAME% | ||||
| 
 | ||||
| @rem Resolve any "." and ".." in APP_HOME to make it shorter. | ||||
| for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi | ||||
| 
 | ||||
| @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. | ||||
| set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" | ||||
| 
 | ||||
| @rem Find java.exe | ||||
| if defined JAVA_HOME goto findJavaFromJavaHome | ||||
| 
 | ||||
| set JAVA_EXE=java.exe | ||||
| %JAVA_EXE% -version >NUL 2>&1 | ||||
| if %ERRORLEVEL% equ 0 goto execute | ||||
| 
 | ||||
| echo. | ||||
| echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. | ||||
| echo. | ||||
| echo Please set the JAVA_HOME variable in your environment to match the | ||||
| echo location of your Java installation. | ||||
| 
 | ||||
| goto fail | ||||
| 
 | ||||
| :findJavaFromJavaHome | ||||
| set JAVA_HOME=%JAVA_HOME:"=% | ||||
| set JAVA_EXE=%JAVA_HOME%/bin/java.exe | ||||
| 
 | ||||
| if exist "%JAVA_EXE%" goto execute | ||||
| 
 | ||||
| echo. | ||||
| echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% | ||||
| echo. | ||||
| echo Please set the JAVA_HOME variable in your environment to match the | ||||
| echo location of your Java installation. | ||||
| 
 | ||||
| goto fail | ||||
| 
 | ||||
| :execute | ||||
| @rem Setup the command line | ||||
| 
 | ||||
| set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar | ||||
| 
 | ||||
| 
 | ||||
| @rem Execute Gradle | ||||
| "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* | ||||
| 
 | ||||
| :end | ||||
| @rem End local scope for the variables with windows NT shell | ||||
| if %ERRORLEVEL% equ 0 goto mainEnd | ||||
| 
 | ||||
| :fail | ||||
| rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of | ||||
| rem the _cmd.exe /c_ return code! | ||||
| set EXIT_CODE=%ERRORLEVEL% | ||||
| if %EXIT_CODE% equ 0 set EXIT_CODE=1 | ||||
| if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% | ||||
| exit /b %EXIT_CODE% | ||||
| 
 | ||||
| :mainEnd | ||||
| if "%OS%"=="Windows_NT" endlocal | ||||
| 
 | ||||
| :omega | ||||
							
								
								
									
										46
									
								
								lib/build.gradle
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								lib/build.gradle
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,46 @@ | ||||
| plugins { | ||||
|     id 'groovy' | ||||
| } | ||||
| 
 | ||||
| repositories { | ||||
|     mavenCentral() | ||||
| } | ||||
| 
 | ||||
| dependencies { | ||||
|     // https://mvnrepository.com/artifact/org.apache.groovy/groovy | ||||
|     implementation 'org.apache.groovy:groovy:4.0.7' | ||||
| 
 | ||||
|     // https://mvnrepository.com/artifact/org.apache.groovy/groovy-templates | ||||
|     implementation 'org.apache.groovy:groovy-templates:4.0.7' | ||||
| 
 | ||||
|     // https://mvnrepository.com/artifact/org.commonmark/commonmark | ||||
|     implementation 'org.commonmark:commonmark:0.21.0' | ||||
| 
 | ||||
|     // https://mvnrepository.com/artifact/org.commonmark/commonmark-ext-yaml-front-matter | ||||
|     implementation 'org.commonmark:commonmark-ext-yaml-front-matter:0.21.0' | ||||
| 
 | ||||
|     // https://mvnrepository.com/artifact/org.slf4j/slf4j-api | ||||
|     implementation 'org.slf4j:slf4j-api:1.7.36' | ||||
| 
 | ||||
|     /** | ||||
|      * TESTING | ||||
|      */ | ||||
|     // https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter-api | ||||
|     testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.2' | ||||
| 
 | ||||
|     // https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter-engine | ||||
|     testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.2' | ||||
| 
 | ||||
|     /** | ||||
|      * Test Logging | ||||
|      */ | ||||
|     // https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-slf4j-impl | ||||
|     testRuntimeOnly 'org.apache.logging.log4j:log4j-slf4j-impl:2.19.0' | ||||
| 
 | ||||
|     // https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-core | ||||
|     testRuntimeOnly 'org.apache.logging.log4j:log4j-core:2.19.0' | ||||
| } | ||||
| 
 | ||||
| test { | ||||
|     useJUnitPlatform() | ||||
| } | ||||
							
								
								
									
										19
									
								
								lib/src/main/groovy/com/jessebrault/ssg/Config.groovy
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								lib/src/main/groovy/com/jessebrault/ssg/Config.groovy
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,19 @@ | ||||
| package com.jessebrault.ssg | ||||
| 
 | ||||
| import com.jessebrault.ssg.frontmatter.FrontMatterGetter | ||||
| import com.jessebrault.ssg.renderer.Renderer | ||||
| import com.jessebrault.ssg.template.TemplatesFactory | ||||
| import com.jessebrault.ssg.textfile.TextFilesFactory | ||||
| import com.jessebrault.ssg.textrenderer.TextRenderer | ||||
| import groovy.transform.Canonical | ||||
| import groovy.transform.MapConstructor | ||||
| 
 | ||||
| @Canonical | ||||
| @MapConstructor | ||||
| class Config { | ||||
|     TextFilesFactory textFilesFactory | ||||
|     TemplatesFactory templatesFactory | ||||
|     FrontMatterGetter markdownFrontMatterGetter | ||||
|     TextRenderer markdownRenderer | ||||
|     Renderer gspRenderer | ||||
| } | ||||
							
								
								
									
										14
									
								
								lib/src/main/groovy/com/jessebrault/ssg/SiteSpec.groovy
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								lib/src/main/groovy/com/jessebrault/ssg/SiteSpec.groovy
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,14 @@ | ||||
| package com.jessebrault.ssg | ||||
| 
 | ||||
| import groovy.transform.Canonical | ||||
| import groovy.transform.MapConstructor | ||||
| 
 | ||||
| @Canonical | ||||
| @MapConstructor | ||||
| class SiteSpec { | ||||
|     File buildDir | ||||
|     File staticDir | ||||
|     File textsDir | ||||
|     File templatesDir | ||||
|     File templatePartsDir | ||||
| } | ||||
| @ -0,0 +1,5 @@ | ||||
| package com.jessebrault.ssg; | ||||
| 
 | ||||
| public interface StaticSiteGenerator { | ||||
|     void generate(SiteSpec spec); | ||||
| } | ||||
| @ -0,0 +1,59 @@ | ||||
| package com.jessebrault.ssg | ||||
| 
 | ||||
| import com.jessebrault.ssg.frontmatter.FrontMatterGetter | ||||
| import com.jessebrault.ssg.template.TemplatesFactory | ||||
| import com.jessebrault.ssg.renderer.Renderer | ||||
| import com.jessebrault.ssg.textfile.TextFile | ||||
| import com.jessebrault.ssg.textfile.TextFilesFactory | ||||
| import com.jessebrault.ssg.textrenderer.TextRenderer | ||||
| import org.slf4j.Logger | ||||
| import org.slf4j.LoggerFactory | ||||
| 
 | ||||
| class StaticSiteGeneratorImpl implements StaticSiteGenerator { | ||||
| 
 | ||||
|     private static final Logger logger = LoggerFactory.getLogger(StaticSiteGeneratorImpl) | ||||
| 
 | ||||
|     private static String stripExtension(String relativePath) { | ||||
|         relativePath.substring(0, relativePath.lastIndexOf('.')) | ||||
|     } | ||||
| 
 | ||||
|     private final TextFilesFactory textFilesFactory | ||||
|     private final TemplatesFactory templateFactory | ||||
|     private final FrontMatterGetter markdownFrontMatterGetter | ||||
|     private final TextRenderer markdownRenderer | ||||
|     private final Renderer gspRenderer | ||||
| 
 | ||||
|     StaticSiteGeneratorImpl(Config config) { | ||||
|         this.textFilesFactory = Objects.requireNonNull(config.textFilesFactory) | ||||
|         this.templateFactory = Objects.requireNonNull(config.templatesFactory) | ||||
|         this.markdownFrontMatterGetter = Objects.requireNonNull(config.markdownFrontMatterGetter) | ||||
|         this.markdownRenderer = Objects.requireNonNull(config.markdownRenderer) | ||||
|         this.gspRenderer = Objects.requireNonNull(config.gspRenderer) | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     void generate(SiteSpec spec) { | ||||
|         def textFiles = this.textFilesFactory.getTextFiles(spec.textsDir) | ||||
|         def templates = this.templateFactory.getTemplates(spec.templatesDir) | ||||
|         textFiles.each { | ||||
|             if (it.type == TextFile.Type.MARKDOWN) { | ||||
|                 def frontMatter = this.markdownFrontMatterGetter.get(it.file.text) | ||||
|                 def desiredTemplate = frontMatter['template'] | ||||
|                 if (desiredTemplate == null) { | ||||
|                     throw new IllegalArgumentException('template must not be null') | ||||
|                 } | ||||
|                 def template = templates.find { it.relativePath == desiredTemplate } | ||||
|                 def renderedText = this.markdownRenderer.render(it.file.text) | ||||
|                 def result = this.gspRenderer.render(template, renderedText) | ||||
|                 def outFile = new File(spec.buildDir, stripExtension(it.relativePath) + '.html') | ||||
|                 if (outFile.exists()) { | ||||
|                     outFile.delete() | ||||
|                 } | ||||
|                 outFile.createParentDirectories() | ||||
|                 logger.debug('writing to outFile {}', outFile) | ||||
|                 outFile.write(result) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,22 @@ | ||||
| package com.jessebrault.ssg.frontmatter | ||||
| 
 | ||||
| import groovy.transform.TupleConstructor | ||||
| 
 | ||||
| @TupleConstructor(includeFields = true) | ||||
| class FrontMatter { | ||||
| 
 | ||||
|     private final Map<String, List<String>> data | ||||
| 
 | ||||
|     String getSingle(String key) { | ||||
|         data[key][0] | ||||
|     } | ||||
| 
 | ||||
|     String getAt(String key) { | ||||
|         this.getSingle(key) | ||||
|     } | ||||
| 
 | ||||
|     List<String> getList(String key) { | ||||
|         data[key] | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,5 @@ | ||||
| package com.jessebrault.ssg.frontmatter; | ||||
| 
 | ||||
| public interface FrontMatterGetter { | ||||
|     FrontMatter get(String text); | ||||
| } | ||||
| @ -0,0 +1,21 @@ | ||||
| package com.jessebrault.ssg.frontmatter | ||||
| 
 | ||||
| import org.commonmark.ext.front.matter.YamlFrontMatterExtension | ||||
| import org.commonmark.ext.front.matter.YamlFrontMatterVisitor | ||||
| import org.commonmark.parser.Parser | ||||
| 
 | ||||
| class MarkdownFrontMatterGetter implements FrontMatterGetter { | ||||
| 
 | ||||
|     private static final Parser parser = Parser.builder() | ||||
|             .extensions([YamlFrontMatterExtension.create()]) | ||||
|             .build() | ||||
| 
 | ||||
|     @Override | ||||
|     FrontMatter get(String text) { | ||||
|         def node = parser.parse(text) | ||||
|         def v = new YamlFrontMatterVisitor() | ||||
|         node.accept(v) | ||||
|         new FrontMatter(v.data) | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,18 @@ | ||||
| package com.jessebrault.ssg.renderer | ||||
| 
 | ||||
| import com.jessebrault.ssg.template.Template | ||||
| import groovy.text.GStringTemplateEngine | ||||
| import groovy.text.TemplateEngine | ||||
| 
 | ||||
| class GspRenderer implements Renderer { | ||||
| 
 | ||||
|     private static final TemplateEngine engine = new GStringTemplateEngine() | ||||
| 
 | ||||
|     @Override | ||||
|     String render(Template template, String text) { | ||||
|         engine.createTemplate(template.file.text).make([ | ||||
|                 text: text | ||||
|         ]) | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,7 @@ | ||||
| package com.jessebrault.ssg.renderer; | ||||
| 
 | ||||
| import com.jessebrault.ssg.template.Template; | ||||
| 
 | ||||
| public interface Renderer { | ||||
|     String render(Template template, String text); | ||||
| } | ||||
| @ -0,0 +1,26 @@ | ||||
| package com.jessebrault.ssg.template | ||||
| 
 | ||||
| import groovy.transform.Canonical | ||||
| 
 | ||||
| @Canonical | ||||
| class Template { | ||||
| 
 | ||||
|     enum Type { | ||||
|         GSP('.gsp'); | ||||
| 
 | ||||
|         private final String extension | ||||
| 
 | ||||
|         Type(String extension) { | ||||
|             this.extension = extension | ||||
|         } | ||||
| 
 | ||||
|         String getExtension() { | ||||
|             this.extension | ||||
|         } | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     File file | ||||
|     String relativePath | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,8 @@ | ||||
| package com.jessebrault.ssg.template; | ||||
| 
 | ||||
| import java.io.File; | ||||
| import java.util.Collection; | ||||
| 
 | ||||
| public interface TemplatesFactory { | ||||
|     Collection<Template> getTemplates(File templatesDir); | ||||
| } | ||||
| @ -0,0 +1,30 @@ | ||||
| package com.jessebrault.ssg.template | ||||
| 
 | ||||
| import org.slf4j.Logger | ||||
| import org.slf4j.LoggerFactory | ||||
| 
 | ||||
| class TemplatesFactoryImpl implements TemplatesFactory { | ||||
| 
 | ||||
|     private static final Logger logger = LoggerFactory.getLogger(TemplatesFactoryImpl) | ||||
| 
 | ||||
|     private static boolean hasTemplateExtension(File file) { | ||||
|         file.name.endsWith('.gsp') | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     Collection<Template> getTemplates(File templatesDir) { | ||||
|         if (!templatesDir.isDirectory()) { | ||||
|             throw new IllegalArgumentException('templatesDir must be a directory') | ||||
|         } | ||||
|         def templates = [] | ||||
|         templatesDir.eachFileRecurse { | ||||
|             if (it.isFile() && hasTemplateExtension(it)) { | ||||
|                 def relativePath = templatesDir.relativePath(it) | ||||
|                 logger.debug('found template {}', relativePath) | ||||
|                 templates << new Template(it, relativePath) | ||||
|             } | ||||
|         } | ||||
|         templates | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,27 @@ | ||||
| package com.jessebrault.ssg.textfile | ||||
| 
 | ||||
| import groovy.transform.Canonical | ||||
| 
 | ||||
| @Canonical | ||||
| class TextFile { | ||||
| 
 | ||||
|     enum Type { | ||||
|         MARKDOWN('.md'); | ||||
| 
 | ||||
|         private final String extension | ||||
| 
 | ||||
|         Type(String extension) { | ||||
|             this.extension = extension | ||||
|         } | ||||
| 
 | ||||
|         String getExtension() { | ||||
|             this.extension | ||||
|         } | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     File file | ||||
|     String relativePath | ||||
|     Type type | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,10 @@ | ||||
| package com.jessebrault.ssg.textfile | ||||
| 
 | ||||
| import com.jessebrault.ssg.textrenderer.TextRenderer | ||||
| import groovy.transform.Canonical | ||||
| 
 | ||||
| @Canonical | ||||
| class TextFileType { | ||||
|     Collection<String> extensions | ||||
|     TextRenderer renderer | ||||
| } | ||||
| @ -0,0 +1,8 @@ | ||||
| package com.jessebrault.ssg.textfile; | ||||
| 
 | ||||
| import java.io.File; | ||||
| import java.util.Collection; | ||||
| 
 | ||||
| public interface TextFilesFactory { | ||||
|     Collection<TextFile> getTextFiles(File textsDir); | ||||
| } | ||||
| @ -0,0 +1,35 @@ | ||||
| package com.jessebrault.ssg.textfile | ||||
| 
 | ||||
| import com.jessebrault.ssg.textfile.TextFile.Type | ||||
| import org.slf4j.Logger | ||||
| import org.slf4j.LoggerFactory | ||||
| 
 | ||||
| class TextFilesFactoryImpl implements TextFilesFactory { | ||||
| 
 | ||||
|     private static final Logger logger = LoggerFactory.getLogger(TextFilesFactoryImpl) | ||||
| 
 | ||||
|     private static Type getType(File file) { | ||||
|         Type.values().find { | ||||
|             file.name.endsWith(it.extension) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     Collection<TextFile> getTextFiles(File textsDir) { | ||||
|         if (!textsDir.isDirectory()) { | ||||
|             throw new IllegalArgumentException('textsDir must be a directory') | ||||
|         } | ||||
| 
 | ||||
|         def textFiles = [] | ||||
|         textsDir.eachFileRecurse { | ||||
|             def type = getType(it) | ||||
|             if (type != null) { | ||||
|                 def relativePath = textsDir.relativePath(it) | ||||
|                 logger.debug('found textFile {}', relativePath) | ||||
|                 textFiles << new TextFile(it, relativePath, type) | ||||
|             } | ||||
|         } | ||||
|         textFiles | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,19 @@ | ||||
| package com.jessebrault.ssg.textrenderer | ||||
| 
 | ||||
| import org.commonmark.ext.front.matter.YamlFrontMatterExtension | ||||
| import org.commonmark.parser.Parser | ||||
| import org.commonmark.renderer.html.HtmlRenderer | ||||
| 
 | ||||
| class MarkdownRenderer implements TextRenderer { | ||||
| 
 | ||||
|     private static final Parser parser = Parser.builder() | ||||
|             .extensions([YamlFrontMatterExtension.create()]) | ||||
|             .build() | ||||
|     private static final HtmlRenderer htmlRenderer = HtmlRenderer.builder().build() | ||||
| 
 | ||||
|     @Override | ||||
|     String render(String text) { | ||||
|         htmlRenderer.render(parser.parse(text)) | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,5 @@ | ||||
| package com.jessebrault.ssg.textrenderer; | ||||
| 
 | ||||
| public interface TextRenderer { | ||||
|     String render(String text); | ||||
| } | ||||
| @ -0,0 +1,75 @@ | ||||
| package com.jessebrault.ssg | ||||
| 
 | ||||
| import com.jessebrault.ssg.frontmatter.MarkdownFrontMatterGetter | ||||
| import com.jessebrault.ssg.renderer.GspRenderer | ||||
| import com.jessebrault.ssg.template.TemplatesFactoryImpl | ||||
| import com.jessebrault.ssg.textfile.TextFilesFactoryImpl | ||||
| import com.jessebrault.ssg.textrenderer.MarkdownRenderer | ||||
| import org.junit.jupiter.api.Test | ||||
| import org.slf4j.Logger | ||||
| import org.slf4j.LoggerFactory | ||||
| 
 | ||||
| import static org.junit.jupiter.api.Assertions.assertEquals | ||||
| import static org.junit.jupiter.api.Assertions.assertTrue | ||||
| 
 | ||||
| class StaticSiteGeneratorTests { | ||||
| 
 | ||||
|     private static final Logger logger = LoggerFactory.getLogger(StaticSiteGeneratorTests) | ||||
| 
 | ||||
|     private final StaticSiteGenerator ssg = new StaticSiteGeneratorImpl(new Config( | ||||
|             textFilesFactory: new TextFilesFactoryImpl(), | ||||
|             templatesFactory: new TemplatesFactoryImpl(), | ||||
|             markdownFrontMatterGetter: new MarkdownFrontMatterGetter(), | ||||
|             markdownRenderer: new MarkdownRenderer(), | ||||
|             gspRenderer: new GspRenderer() | ||||
|     )) | ||||
| 
 | ||||
|     @Test | ||||
|     void simple() { | ||||
|         def buildDir = File.createTempDir() | ||||
|         def textsDir = File.createTempDir() | ||||
|         def templatesDir = File.createTempDir() | ||||
| 
 | ||||
|         new File(textsDir, 'test.md').write('---\ntemplate: test.gsp\n---\n**Hello, World!**') | ||||
|         new File(templatesDir, 'test.gsp').write('<%= text %>') | ||||
| 
 | ||||
| 
 | ||||
|         def spec = new SiteSpec( | ||||
|                 buildDir: buildDir, | ||||
|                 textsDir: textsDir, | ||||
|                 templatesDir: templatesDir | ||||
|         ) | ||||
| 
 | ||||
|         this.ssg.generate(spec) | ||||
| 
 | ||||
|         def outFile = new File(buildDir, 'test.html') | ||||
|         assertTrue(outFile.exists()) | ||||
|         assertEquals('<p><strong>Hello, World!</strong></p>\n', outFile.text) | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     void nested() { | ||||
|         def buildDir = File.createTempDir() | ||||
|         def textsDir = File.createTempDir() | ||||
|         def templatesDir = File.createTempDir() | ||||
| 
 | ||||
|         new FileTreeBuilder(textsDir).with { | ||||
|             dir('nested') { | ||||
|                 file('nested.md', '---\ntemplate: nested.gsp\n---\n**Hello, World!**') | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         new File(templatesDir, 'nested.gsp').write('<%= text %>') | ||||
| 
 | ||||
|         this.ssg.generate(new SiteSpec( | ||||
|                 buildDir: buildDir, | ||||
|                 textsDir: textsDir, | ||||
|                 templatesDir: templatesDir | ||||
|         )) | ||||
| 
 | ||||
|         def outFile = new File(new File(buildDir, 'nested'), 'nested.html') | ||||
|         assertTrue(outFile.exists()) | ||||
|         assertEquals('<p><strong>Hello, World!</strong></p>\n', outFile.text) | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,51 @@ | ||||
| package com.jessebrault.ssg.template | ||||
| 
 | ||||
| import org.junit.jupiter.api.Test | ||||
| 
 | ||||
| import static org.junit.jupiter.api.Assertions.assertEquals | ||||
| 
 | ||||
| class TemplatesFactoryTests { | ||||
| 
 | ||||
|     private final TemplatesFactory templateFactory = new TemplatesFactoryImpl() | ||||
| 
 | ||||
|     @Test | ||||
|     void findsTemplate() { | ||||
|         def templatesDir = File.createTempDir() | ||||
|         def templateFile = new File(templatesDir, 'test.gsp') | ||||
|         templateFile.write('<% out << text %>') | ||||
| 
 | ||||
|         def r = this.templateFactory.getTemplates(templatesDir) | ||||
|         assertEquals(1, r.size()) | ||||
|         def t0 = r[0] | ||||
|         assertEquals('test.gsp', t0.relativePath) | ||||
|         assertEquals('<% out << text %>', t0.file.text) | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     void findsNestedTemplate() { | ||||
|         def templatesDir = File.createTempDir() | ||||
|         new FileTreeBuilder(templatesDir).with { | ||||
|             dir('nested') { | ||||
|                 file('nested.gsp', '<%= text %>') | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         def r = this.templateFactory.getTemplates(templatesDir) | ||||
|         assertEquals(1, r.size()) | ||||
|         def t0 = r[0] | ||||
|         assertEquals('nested/nested.gsp', t0.relativePath) | ||||
|         assertEquals('<%= text %>', t0.file.text) | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     void ignoresUnsupportedFile() { | ||||
|         def templatesDir = File.createTempDir() | ||||
|         new File(templatesDir, '.ignored').with { | ||||
|             write('Ignored!') | ||||
|         } | ||||
| 
 | ||||
|         def r = this.templateFactory.getTemplates(templatesDir) | ||||
|         assertEquals(0, r.size()) | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,51 @@ | ||||
| package com.jessebrault.ssg.textfile | ||||
| 
 | ||||
| import org.junit.jupiter.api.Test | ||||
| 
 | ||||
| import static org.junit.jupiter.api.Assertions.assertEquals | ||||
| 
 | ||||
| class TextsFilesFactoryTests { | ||||
| 
 | ||||
|     private final TextFilesFactory textFilesFactory = new TextFilesFactoryImpl() | ||||
| 
 | ||||
|     @Test | ||||
|     void findsFile() { | ||||
|         def baseDir = File.createTempDir() | ||||
|         def ftb = new FileTreeBuilder(baseDir) | ||||
|         ftb.file('test.md', '**Hello, World!**') | ||||
| 
 | ||||
|         def r = textFilesFactory.getTextFiles(baseDir) | ||||
|         assertEquals(1, r.size()) | ||||
|         def f0 = r[0] | ||||
|         assertEquals('test.md', f0.relativePath) | ||||
|         assertEquals('**Hello, World!**', f0.file.text) | ||||
|         assertEquals(TextFile.Type.MARKDOWN, f0.type) | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     void findsNestedFiles() { | ||||
|         def baseDir = File.createTempDir() | ||||
|         def ftb = new FileTreeBuilder(baseDir) | ||||
|         ftb.dir('nested') { | ||||
|             file('nested.md', '**Hello!**') | ||||
|         } | ||||
| 
 | ||||
|         def r = textFilesFactory.getTextFiles(baseDir) | ||||
|         assertEquals(1, r.size()) | ||||
|         def f0 = r[0] | ||||
|         assertEquals('nested/nested.md', f0.relativePath) | ||||
|         assertEquals('**Hello!**', f0.file.text) | ||||
|         assertEquals(TextFile.Type.MARKDOWN, f0.type) | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     void ignoresUnsupportedFile() { | ||||
|         def baseDir = File.createTempDir() | ||||
|         def ftb = new FileTreeBuilder(baseDir) | ||||
|         ftb.file('.ignored', 'Ignored!') | ||||
| 
 | ||||
|         def r = textFilesFactory.getTextFiles(baseDir) | ||||
|         assertEquals(0, r.size()) | ||||
|     } | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										17
									
								
								lib/src/test/resources/log4j2.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								lib/src/test/resources/log4j2.xml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,17 @@ | ||||
| <?xml version="1.0" encoding="UTF-8" ?> | ||||
| <Configuration name="fauxtex" status="WARN"> | ||||
|     <Appenders> | ||||
|         <Console name="standard" target="SYSTEM_OUT"> | ||||
|             <PatternLayout> | ||||
|                 <MarkerPatternSelector defaultPattern="%-5level %logger{1}: %msg%n%ex"> | ||||
|                     <PatternMatch key="FLOW" pattern="%-5level %logger{1} %M %L: %markerSimpleName %msg%n%ex" /> | ||||
|                 </MarkerPatternSelector> | ||||
|             </PatternLayout> | ||||
|         </Console> | ||||
|     </Appenders> | ||||
|     <Loggers> | ||||
|         <Root level="trace"> | ||||
|             <AppenderRef ref="standard" /> | ||||
|         </Root> | ||||
|     </Loggers> | ||||
| </Configuration> | ||||
							
								
								
									
										2
									
								
								settings.gradle
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								settings.gradle
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,2 @@ | ||||
| rootProject.name = 'ssg' | ||||
| include 'cli', 'lib' | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user
	 Jesse Brault
						Jesse Brault