Cli much better interface and user experience.
This commit is contained in:
parent
0b95f4662d
commit
90db278e39
@ -0,0 +1,89 @@
|
|||||||
|
package com.jessebrault.ssg
|
||||||
|
|
||||||
|
import com.jessebrault.ssg.buildscript.GroovyBuildScriptRunner
|
||||||
|
import com.jessebrault.ssg.part.GspPartRenderer
|
||||||
|
import com.jessebrault.ssg.part.PartFilePartsProvider
|
||||||
|
import com.jessebrault.ssg.part.PartType
|
||||||
|
import com.jessebrault.ssg.specialpage.GspSpecialPageRenderer
|
||||||
|
import com.jessebrault.ssg.specialpage.SpecialPageFileSpecialPagesProvider
|
||||||
|
import com.jessebrault.ssg.specialpage.SpecialPageType
|
||||||
|
import com.jessebrault.ssg.template.GspTemplateRenderer
|
||||||
|
import com.jessebrault.ssg.template.TemplateFileTemplatesProvider
|
||||||
|
import com.jessebrault.ssg.template.TemplateType
|
||||||
|
import com.jessebrault.ssg.text.MarkdownExcerptGetter
|
||||||
|
import com.jessebrault.ssg.text.MarkdownFrontMatterGetter
|
||||||
|
import com.jessebrault.ssg.text.MarkdownTextRenderer
|
||||||
|
import com.jessebrault.ssg.text.TextFileTextsProvider
|
||||||
|
import com.jessebrault.ssg.text.TextType
|
||||||
|
import org.apache.logging.log4j.LogManager
|
||||||
|
import org.apache.logging.log4j.Logger
|
||||||
|
|
||||||
|
abstract class AbstractBuildCommand extends AbstractSubCommand {
|
||||||
|
|
||||||
|
private static final Logger logger = LogManager.getLogger(AbstractBuildCommand)
|
||||||
|
|
||||||
|
protected final Collection<Build> builds = []
|
||||||
|
protected final StaticSiteGenerator ssg
|
||||||
|
|
||||||
|
AbstractBuildCommand() {
|
||||||
|
// Configure
|
||||||
|
def markdownText = new TextType(['.md'], new MarkdownTextRenderer(), new MarkdownFrontMatterGetter(), new MarkdownExcerptGetter())
|
||||||
|
def gspTemplate = new TemplateType(['.gsp'], new GspTemplateRenderer())
|
||||||
|
def gspPart = new PartType(['.gsp'], new GspPartRenderer())
|
||||||
|
def gspSpecialPage = new SpecialPageType(['.gsp'], new GspSpecialPageRenderer())
|
||||||
|
|
||||||
|
def defaultTextsProvider = new TextFileTextsProvider([markdownText], new File('texts'))
|
||||||
|
def defaultTemplatesProvider = new TemplateFileTemplatesProvider([gspTemplate], new File('templates'))
|
||||||
|
def defaultPartsProvider = new PartFilePartsProvider([gspPart], new File('parts'))
|
||||||
|
def defaultSpecialPagesProvider = new SpecialPageFileSpecialPagesProvider([gspSpecialPage], new File('specialPages'))
|
||||||
|
|
||||||
|
def defaultConfig = new Config(
|
||||||
|
textProviders: [defaultTextsProvider],
|
||||||
|
templatesProviders: [defaultTemplatesProvider],
|
||||||
|
partsProviders: [defaultPartsProvider],
|
||||||
|
specialPagesProviders: [defaultSpecialPagesProvider]
|
||||||
|
)
|
||||||
|
def defaultGlobals = [:]
|
||||||
|
|
||||||
|
// Run build script, if applicable
|
||||||
|
if (new File('ssgBuilds.groovy').exists()) {
|
||||||
|
logger.info('found buildScript: ssgBuilds.groovy')
|
||||||
|
def buildScriptRunner = new GroovyBuildScriptRunner()
|
||||||
|
this.builds.addAll(buildScriptRunner.runBuildScript('ssgBuilds.groovy', defaultConfig, defaultGlobals))
|
||||||
|
logger.debug('after running ssgBuilds.groovy, builds: {}', this.builds)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.builds.empty) {
|
||||||
|
// Add default build
|
||||||
|
builds << new Build('default', defaultConfig, defaultGlobals, new File('build'))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get ssg object
|
||||||
|
this.ssg = new SimpleStaticSiteGenerator()
|
||||||
|
}
|
||||||
|
|
||||||
|
protected final Integer doBuild() {
|
||||||
|
logger.traceEntry('builds: {}, ssg: {}', this.builds, this.ssg)
|
||||||
|
|
||||||
|
def hadDiagnostics = false
|
||||||
|
// Do each build
|
||||||
|
this.builds.each {
|
||||||
|
def result = this.ssg.generate(it)
|
||||||
|
if (result.v1.size() > 0) {
|
||||||
|
hadDiagnostics = true
|
||||||
|
result.v1.each {
|
||||||
|
logger.error(it.message)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
result.v2.each { GeneratedPage generatedPage ->
|
||||||
|
def target = new File(it.outDir, generatedPage.path + '.html')
|
||||||
|
target.createParentDirectories()
|
||||||
|
target.write(generatedPage.html)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.traceExit(hadDiagnostics ? 1 : 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,45 @@
|
|||||||
|
package com.jessebrault.ssg
|
||||||
|
|
||||||
|
import org.apache.logging.log4j.Level
|
||||||
|
import org.apache.logging.log4j.LogManager
|
||||||
|
import org.apache.logging.log4j.Logger
|
||||||
|
import org.apache.logging.log4j.core.LoggerContext
|
||||||
|
import picocli.CommandLine
|
||||||
|
|
||||||
|
import java.util.concurrent.Callable
|
||||||
|
|
||||||
|
abstract class AbstractSubCommand implements Callable<Integer> {
|
||||||
|
|
||||||
|
private static final Logger logger = LogManager.getLogger(AbstractSubCommand)
|
||||||
|
|
||||||
|
@CommandLine.ParentCommand
|
||||||
|
StaticSiteGeneratorCli cli
|
||||||
|
|
||||||
|
abstract Integer doSubCommand()
|
||||||
|
|
||||||
|
@Override
|
||||||
|
Integer call() {
|
||||||
|
logger.traceEntry()
|
||||||
|
|
||||||
|
// Setup Loggers
|
||||||
|
def context = (LoggerContext) LogManager.getContext(false)
|
||||||
|
def configuration = context.getConfiguration()
|
||||||
|
def rootLoggerConfig = configuration.getRootLogger()
|
||||||
|
|
||||||
|
if (this.cli.logLevel?.info) {
|
||||||
|
rootLoggerConfig.level = Level.INFO
|
||||||
|
} else if (this.cli.logLevel?.debug) {
|
||||||
|
rootLoggerConfig.level = Level.DEBUG
|
||||||
|
} else if (this.cli.logLevel?.trace) {
|
||||||
|
rootLoggerConfig.level = Level.TRACE
|
||||||
|
} else {
|
||||||
|
rootLoggerConfig.level = Level.WARN
|
||||||
|
}
|
||||||
|
|
||||||
|
context.updateLoggers()
|
||||||
|
|
||||||
|
// Run SubCommand
|
||||||
|
logger.traceExit(this.doSubCommand())
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
21
cli/src/main/groovy/com/jessebrault/ssg/SsgBuild.groovy
Normal file
21
cli/src/main/groovy/com/jessebrault/ssg/SsgBuild.groovy
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
package com.jessebrault.ssg
|
||||||
|
|
||||||
|
import org.apache.logging.log4j.LogManager
|
||||||
|
import org.apache.logging.log4j.Logger
|
||||||
|
import picocli.CommandLine
|
||||||
|
|
||||||
|
@CommandLine.Command(
|
||||||
|
name = 'build',
|
||||||
|
mixinStandardHelpOptions = true,
|
||||||
|
description = 'Builds the project.'
|
||||||
|
)
|
||||||
|
class SsgBuild extends AbstractBuildCommand {
|
||||||
|
|
||||||
|
private static final Logger logger = LogManager.getLogger(SsgBuild)
|
||||||
|
|
||||||
|
@Override
|
||||||
|
Integer doSubCommand() {
|
||||||
|
this.doBuild()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
54
cli/src/main/groovy/com/jessebrault/ssg/SsgInit.groovy
Normal file
54
cli/src/main/groovy/com/jessebrault/ssg/SsgInit.groovy
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
package com.jessebrault.ssg
|
||||||
|
|
||||||
|
import org.apache.logging.log4j.LogManager
|
||||||
|
import org.apache.logging.log4j.Logger
|
||||||
|
import picocli.CommandLine
|
||||||
|
|
||||||
|
@CommandLine.Command(
|
||||||
|
name = 'init',
|
||||||
|
mixinStandardHelpOptions = true,
|
||||||
|
description = 'Generates a blank project, optionally with some basic files.'
|
||||||
|
)
|
||||||
|
class SsgInit extends AbstractSubCommand {
|
||||||
|
|
||||||
|
private static final Logger logger = LogManager.getLogger(SsgInit)
|
||||||
|
|
||||||
|
@CommandLine.Option(names = ['-s', '--skeleton'], description = 'Include some basic files in the generated project.')
|
||||||
|
boolean withSkeletonFiles
|
||||||
|
|
||||||
|
@Override
|
||||||
|
Integer doSubCommand() {
|
||||||
|
logger.traceEntry()
|
||||||
|
new FileTreeBuilder().with {
|
||||||
|
// Generate dirs
|
||||||
|
dir('texts') {
|
||||||
|
if (this.withSkeletonFiles) {
|
||||||
|
file('hello.md', this.getClass().getResource('/hello.md').text)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dir('templates') {
|
||||||
|
if (this.withSkeletonFiles) {
|
||||||
|
file('hello.gsp', this.getClass().getResource('/hello.gsp').text)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dir('parts') {
|
||||||
|
if (this.withSkeletonFiles) {
|
||||||
|
file('head.gsp', this.getClass().getResource('/head.gsp').text)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dir('specialPages') {
|
||||||
|
if (this.withSkeletonFiles) {
|
||||||
|
file('specialPage.gsp', this.getClass().getResource('/specialPage.gsp').text)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate ssgBuilds.groovy
|
||||||
|
if (this.withSkeletonFiles) {
|
||||||
|
file('ssgBuilds.groovy', this.getClass().getResource('/ssgBuilds.groovy').text)
|
||||||
|
} else {
|
||||||
|
file('ssgBuilds.groovy', this.getClass().getResource('/ssgBuildsBasic.groovy').text)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
logger.traceExit(0)
|
||||||
|
}
|
||||||
|
}
|
116
cli/src/main/groovy/com/jessebrault/ssg/SsgWatch.groovy
Normal file
116
cli/src/main/groovy/com/jessebrault/ssg/SsgWatch.groovy
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
package com.jessebrault.ssg
|
||||||
|
|
||||||
|
import com.jessebrault.ssg.provider.WithWatchableDir
|
||||||
|
import groovy.io.FileType
|
||||||
|
import org.apache.logging.log4j.LogManager
|
||||||
|
import org.apache.logging.log4j.Logger
|
||||||
|
import picocli.CommandLine
|
||||||
|
|
||||||
|
import java.nio.file.FileSystems
|
||||||
|
import java.nio.file.Files
|
||||||
|
import java.nio.file.Path
|
||||||
|
import java.nio.file.StandardWatchEventKinds
|
||||||
|
import java.nio.file.WatchEvent
|
||||||
|
import java.nio.file.WatchKey
|
||||||
|
|
||||||
|
@CommandLine.Command(
|
||||||
|
name = 'watch',
|
||||||
|
mixinStandardHelpOptions = true,
|
||||||
|
description = 'Run in watch mode, rebuilding the project whenever files are created/updated/deleted.'
|
||||||
|
)
|
||||||
|
class SsgWatch extends AbstractBuildCommand {
|
||||||
|
|
||||||
|
private static final Logger logger = LogManager.getLogger(SsgWatch)
|
||||||
|
|
||||||
|
@Override
|
||||||
|
Integer doSubCommand() {
|
||||||
|
logger.traceEntry()
|
||||||
|
|
||||||
|
// Setup watchService and watchKeys
|
||||||
|
def watchService = FileSystems.getDefault().newWatchService()
|
||||||
|
Map<WatchKey, Path> watchKeys = [:]
|
||||||
|
|
||||||
|
// Our Closure to register a directory path
|
||||||
|
def registerPath = { Path path ->
|
||||||
|
if (!Files.isDirectory(path)) {
|
||||||
|
throw new IllegalArgumentException('path must be a directory, given: ' + path)
|
||||||
|
}
|
||||||
|
logger.debug('registering dir with path: {}', path)
|
||||||
|
def watchKey = path.register(
|
||||||
|
watchService,
|
||||||
|
StandardWatchEventKinds.ENTRY_CREATE,
|
||||||
|
StandardWatchEventKinds.ENTRY_DELETE,
|
||||||
|
StandardWatchEventKinds.ENTRY_MODIFY
|
||||||
|
)
|
||||||
|
watchKeys[watchKey] = path
|
||||||
|
logger.debug('watchKeys: {}', watchKeys)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get all base watchableDirs
|
||||||
|
Collection<WithWatchableDir> watchableProviders = []
|
||||||
|
this.builds.each {
|
||||||
|
it.config.textProviders.each {
|
||||||
|
if (it instanceof WithWatchableDir) {
|
||||||
|
watchableProviders << it
|
||||||
|
}
|
||||||
|
}
|
||||||
|
it.config.templatesProviders.each {
|
||||||
|
if (it instanceof WithWatchableDir) {
|
||||||
|
watchableProviders << it
|
||||||
|
}
|
||||||
|
}
|
||||||
|
it.config.partsProviders.each {
|
||||||
|
if (it instanceof WithWatchableDir) {
|
||||||
|
watchableProviders << it
|
||||||
|
}
|
||||||
|
}
|
||||||
|
it.config.specialPagesProviders.each {
|
||||||
|
if (it instanceof WithWatchableDir) {
|
||||||
|
watchableProviders << it
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// register them and their child directories using the Closure above
|
||||||
|
watchableProviders.each {
|
||||||
|
def baseDirFile = it.watchableDir
|
||||||
|
registerPath(baseDirFile.toPath())
|
||||||
|
baseDirFile.eachFile(FileType.DIRECTORIES) {
|
||||||
|
registerPath(it.toPath())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//noinspection GroovyInfiniteLoopStatement
|
||||||
|
while (true) {
|
||||||
|
def watchKey = watchService.take()
|
||||||
|
def path = watchKeys[watchKey]
|
||||||
|
if (path == null) {
|
||||||
|
logger.warn('unexpected watchKey: {}', watchKey)
|
||||||
|
} else {
|
||||||
|
watchKey.pollEvents().each {
|
||||||
|
assert it instanceof WatchEvent<Path>
|
||||||
|
def childName = it.context()
|
||||||
|
def childPath = path.resolve(childName)
|
||||||
|
if (it.kind() == StandardWatchEventKinds.ENTRY_CREATE && Files.isDirectory(childPath)) {
|
||||||
|
registerPath(childPath)
|
||||||
|
} else if (Files.isRegularFile(childPath)) {
|
||||||
|
logger.debug('detected {} for regularFile with path {}', it.kind(), childPath)
|
||||||
|
def t = new Thread({
|
||||||
|
this.doBuild()
|
||||||
|
})
|
||||||
|
t.setName('workerThread')
|
||||||
|
t.start()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
def valid = watchKey.reset()
|
||||||
|
if (!valid) {
|
||||||
|
def removedPath = watchKeys.remove(watchKey)
|
||||||
|
logger.debug('removed path: {}', removedPath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//noinspection GroovyUnreachableStatement
|
||||||
|
logger.traceExit(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,45 +1,15 @@
|
|||||||
package com.jessebrault.ssg
|
package com.jessebrault.ssg
|
||||||
|
|
||||||
import com.jessebrault.ssg.buildscript.GroovyBuildScriptRunner
|
|
||||||
import com.jessebrault.ssg.part.GspPartRenderer
|
|
||||||
import com.jessebrault.ssg.part.PartFilePartsProvider
|
|
||||||
import com.jessebrault.ssg.part.PartType
|
|
||||||
import com.jessebrault.ssg.provider.WithWatchableDir
|
|
||||||
import com.jessebrault.ssg.specialpage.GspSpecialPageRenderer
|
|
||||||
import com.jessebrault.ssg.specialpage.SpecialPageFileSpecialPagesProvider
|
|
||||||
import com.jessebrault.ssg.specialpage.SpecialPageType
|
|
||||||
import com.jessebrault.ssg.template.GspTemplateRenderer
|
|
||||||
import com.jessebrault.ssg.template.TemplateFileTemplatesProvider
|
|
||||||
import com.jessebrault.ssg.template.TemplateType
|
|
||||||
import com.jessebrault.ssg.text.MarkdownExcerptGetter
|
|
||||||
import com.jessebrault.ssg.text.MarkdownFrontMatterGetter
|
|
||||||
import com.jessebrault.ssg.text.MarkdownTextRenderer
|
|
||||||
import com.jessebrault.ssg.text.TextFileTextsProvider
|
|
||||||
import com.jessebrault.ssg.text.TextType
|
|
||||||
import groovy.io.FileType
|
|
||||||
import org.apache.logging.log4j.Level
|
|
||||||
import org.apache.logging.log4j.LogManager
|
|
||||||
import org.apache.logging.log4j.Logger
|
|
||||||
import org.apache.logging.log4j.core.LoggerContext
|
|
||||||
import picocli.CommandLine
|
import picocli.CommandLine
|
||||||
|
|
||||||
import java.nio.file.FileSystems
|
|
||||||
import java.nio.file.Files
|
|
||||||
import java.nio.file.Path
|
|
||||||
import java.nio.file.StandardWatchEventKinds
|
|
||||||
import java.nio.file.WatchEvent
|
|
||||||
import java.nio.file.WatchKey
|
|
||||||
import java.util.concurrent.Callable
|
|
||||||
|
|
||||||
@CommandLine.Command(
|
@CommandLine.Command(
|
||||||
name = 'ssg',
|
name = 'ssg',
|
||||||
mixinStandardHelpOptions = true,
|
mixinStandardHelpOptions = true,
|
||||||
version = '0.0.1-SNAPSHOT',
|
version = '0.0.1-SNAPSHOT',
|
||||||
description = 'Generates a set of html files from a given configuration.'
|
description = 'Generates a set of html files from a given configuration.',
|
||||||
|
subcommands = [SsgInit, SsgBuild, SsgWatch]
|
||||||
)
|
)
|
||||||
class StaticSiteGeneratorCli implements Callable<Integer> {
|
class StaticSiteGeneratorCli {
|
||||||
|
|
||||||
private static final Logger logger = LogManager.getLogger(StaticSiteGeneratorCli)
|
|
||||||
|
|
||||||
static void main(String[] args) {
|
static void main(String[] args) {
|
||||||
System.exit(new CommandLine(StaticSiteGeneratorCli).execute(args))
|
System.exit(new CommandLine(StaticSiteGeneratorCli).execute(args))
|
||||||
@ -61,187 +31,4 @@ class StaticSiteGeneratorCli implements Callable<Integer> {
|
|||||||
@CommandLine.ArgGroup(exclusive = true, heading = 'Log Level')
|
@CommandLine.ArgGroup(exclusive = true, heading = 'Log Level')
|
||||||
LogLevel logLevel
|
LogLevel logLevel
|
||||||
|
|
||||||
@CommandLine.Option(names = ['-w', '--watch'], description = 'Run in watch mode.')
|
|
||||||
boolean watch
|
|
||||||
|
|
||||||
@Override
|
|
||||||
Integer call() {
|
|
||||||
logger.traceEntry()
|
|
||||||
|
|
||||||
// Setup Loggers
|
|
||||||
def context = (LoggerContext) LogManager.getContext(false)
|
|
||||||
def configuration = context.getConfiguration()
|
|
||||||
def rootLoggerConfig = configuration.getRootLogger()
|
|
||||||
|
|
||||||
if (this.logLevel?.info) {
|
|
||||||
rootLoggerConfig.level = Level.INFO
|
|
||||||
} else if (this.logLevel?.debug) {
|
|
||||||
rootLoggerConfig.level = Level.DEBUG
|
|
||||||
} else if (this.logLevel?.trace) {
|
|
||||||
rootLoggerConfig.level = Level.TRACE
|
|
||||||
} else {
|
|
||||||
rootLoggerConfig.level = Level.WARN
|
|
||||||
}
|
|
||||||
|
|
||||||
context.updateLoggers()
|
|
||||||
|
|
||||||
// Configure
|
|
||||||
def markdownText = new TextType(['.md'], new MarkdownTextRenderer(), new MarkdownFrontMatterGetter(), new MarkdownExcerptGetter())
|
|
||||||
def gspTemplate = new TemplateType(['.gsp'], new GspTemplateRenderer())
|
|
||||||
def gspPart = new PartType(['.gsp'], new GspPartRenderer())
|
|
||||||
def gspSpecialPage = new SpecialPageType(['.gsp'], new GspSpecialPageRenderer())
|
|
||||||
|
|
||||||
def defaultTextsProvider = new TextFileTextsProvider([markdownText], new File('texts'))
|
|
||||||
def defaultTemplatesProvider = new TemplateFileTemplatesProvider([gspTemplate], new File('templates'))
|
|
||||||
def defaultPartsProvider = new PartFilePartsProvider([gspPart], new File('parts'))
|
|
||||||
def defaultSpecialPagesProvider = new SpecialPageFileSpecialPagesProvider([gspSpecialPage], new File('specialPages'))
|
|
||||||
|
|
||||||
def defaultConfig = new Config(
|
|
||||||
textProviders: [defaultTextsProvider],
|
|
||||||
templatesProviders: [defaultTemplatesProvider],
|
|
||||||
partsProviders: [defaultPartsProvider],
|
|
||||||
specialPagesProviders: [defaultSpecialPagesProvider]
|
|
||||||
)
|
|
||||||
def defaultGlobals = [:]
|
|
||||||
|
|
||||||
Collection<Build> builds = []
|
|
||||||
|
|
||||||
// Run build script, if applicable
|
|
||||||
if (new File('ssgBuilds.groovy').exists()) {
|
|
||||||
logger.info('found buildScript: ssgBuilds.groovy')
|
|
||||||
def buildScriptRunner = new GroovyBuildScriptRunner()
|
|
||||||
builds.addAll(buildScriptRunner.runBuildScript('ssgBuilds.groovy', defaultConfig, defaultGlobals))
|
|
||||||
logger.debug('after running ssgBuilds.groovy, builds: {}', builds)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (builds.empty) {
|
|
||||||
// Add default build
|
|
||||||
builds << new Build('default', defaultConfig, defaultGlobals, new File('build'))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get ssg object
|
|
||||||
def ssg = new SimpleStaticSiteGenerator()
|
|
||||||
|
|
||||||
if (this.watch) {
|
|
||||||
generate(builds, ssg)
|
|
||||||
watch(builds, ssg)
|
|
||||||
} else {
|
|
||||||
generate(builds, ssg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Integer generate(Collection<Build> builds, StaticSiteGenerator ssg) {
|
|
||||||
logger.traceEntry('builds: {}, ssg: {}', builds, ssg)
|
|
||||||
|
|
||||||
def hadDiagnostics = false
|
|
||||||
// Do each build
|
|
||||||
builds.each {
|
|
||||||
def result = ssg.generate(it)
|
|
||||||
if (result.v1.size() > 0) {
|
|
||||||
hadDiagnostics = true
|
|
||||||
result.v1.each {
|
|
||||||
logger.error(it.message)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
result.v2.each { GeneratedPage generatedPage ->
|
|
||||||
def target = new File(it.outDir, generatedPage.path + '.html')
|
|
||||||
target.createParentDirectories()
|
|
||||||
target.write(generatedPage.html)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.traceExit(hadDiagnostics ? 1 : 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Integer watch(Collection<Build> builds, StaticSiteGenerator ssg) {
|
|
||||||
logger.traceEntry('builds: {}, ssg: {}', builds, ssg)
|
|
||||||
|
|
||||||
// Setup watchService and watchKeys
|
|
||||||
def watchService = FileSystems.getDefault().newWatchService()
|
|
||||||
Map<WatchKey, Path> watchKeys = [:]
|
|
||||||
|
|
||||||
// Our Closure to register a directory path
|
|
||||||
def registerPath = { Path path ->
|
|
||||||
if (!Files.isDirectory(path)) {
|
|
||||||
throw new IllegalArgumentException('path must be a directory, given: ' + path)
|
|
||||||
}
|
|
||||||
logger.debug('registering dir with path: {}', path)
|
|
||||||
def watchKey = path.register(
|
|
||||||
watchService,
|
|
||||||
StandardWatchEventKinds.ENTRY_CREATE,
|
|
||||||
StandardWatchEventKinds.ENTRY_DELETE,
|
|
||||||
StandardWatchEventKinds.ENTRY_MODIFY
|
|
||||||
)
|
|
||||||
watchKeys[watchKey] = path
|
|
||||||
logger.debug('watchKeys: {}', watchKeys)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get all base watchableDirs
|
|
||||||
Collection<WithWatchableDir> watchableProviders = []
|
|
||||||
builds.each {
|
|
||||||
it.config.textProviders.each {
|
|
||||||
if (it instanceof WithWatchableDir) {
|
|
||||||
watchableProviders << it
|
|
||||||
}
|
|
||||||
}
|
|
||||||
it.config.templatesProviders.each {
|
|
||||||
if (it instanceof WithWatchableDir) {
|
|
||||||
watchableProviders << it
|
|
||||||
}
|
|
||||||
}
|
|
||||||
it.config.partsProviders.each {
|
|
||||||
if (it instanceof WithWatchableDir) {
|
|
||||||
watchableProviders << it
|
|
||||||
}
|
|
||||||
}
|
|
||||||
it.config.specialPagesProviders.each {
|
|
||||||
if (it instanceof WithWatchableDir) {
|
|
||||||
watchableProviders << it
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// register them and their child directories using the Closure above
|
|
||||||
watchableProviders.each {
|
|
||||||
def baseDirFile = it.watchableDir
|
|
||||||
registerPath(baseDirFile.toPath())
|
|
||||||
baseDirFile.eachFile(FileType.DIRECTORIES) {
|
|
||||||
registerPath(it.toPath())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//noinspection GroovyInfiniteLoopStatement
|
|
||||||
while (true) {
|
|
||||||
def watchKey = watchService.take()
|
|
||||||
def path = watchKeys[watchKey]
|
|
||||||
if (path == null) {
|
|
||||||
logger.warn('unexpected watchKey: {}', watchKey)
|
|
||||||
} else {
|
|
||||||
watchKey.pollEvents().each {
|
|
||||||
assert it instanceof WatchEvent<Path>
|
|
||||||
def childName = it.context()
|
|
||||||
def childPath = path.resolve(childName)
|
|
||||||
if (it.kind() == StandardWatchEventKinds.ENTRY_CREATE && Files.isDirectory(childPath)) {
|
|
||||||
registerPath(childPath)
|
|
||||||
} else if (Files.isRegularFile(childPath)) {
|
|
||||||
logger.debug('detected {} for regularFile with path {}', it.kind(), childPath)
|
|
||||||
def t = new Thread({
|
|
||||||
generate(builds, ssg)
|
|
||||||
})
|
|
||||||
t.setName('workerThread')
|
|
||||||
t.start()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
def valid = watchKey.reset()
|
|
||||||
if (!valid) {
|
|
||||||
def removedPath = watchKeys.remove(watchKey)
|
|
||||||
logger.debug('removed path: {}', removedPath)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//noinspection GroovyUnreachableStatement
|
|
||||||
logger.traceExit(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
3
cli/src/main/resources/head.gsp
Normal file
3
cli/src/main/resources/head.gsp
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
<head>
|
||||||
|
<title><%= binding.title %></title>
|
||||||
|
</head>
|
10
cli/src/main/resources/hello.gsp
Normal file
10
cli/src/main/resources/hello.gsp
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<html>
|
||||||
|
<%
|
||||||
|
out << parts['head.gsp'].render([
|
||||||
|
title: "${ globals.siteTitle }: ${ frontMatter.title }"
|
||||||
|
])
|
||||||
|
%>
|
||||||
|
<body>
|
||||||
|
<%= text %>
|
||||||
|
</body>
|
||||||
|
</html>
|
5
cli/src/main/resources/hello.md
Normal file
5
cli/src/main/resources/hello.md
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
template: hello.gsp
|
||||||
|
title: Greeting
|
||||||
|
---
|
||||||
|
# Hello, World!
|
8
cli/src/main/resources/specialPage.gsp
Normal file
8
cli/src/main/resources/specialPage.gsp
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>${ globals.siteTitle }: Special Page</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<%= texts.find { it.path == 'hello' }.render() %>
|
||||||
|
</body>
|
||||||
|
</html>
|
14
cli/src/main/resources/ssgBuilds.groovy
Normal file
14
cli/src/main/resources/ssgBuilds.groovy
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
// This file was auto-generated by the ssg init command.
|
||||||
|
|
||||||
|
build {
|
||||||
|
name = 'My Simple Build'
|
||||||
|
outDir = new File('mySimpleBuild')
|
||||||
|
|
||||||
|
config {
|
||||||
|
// Config options here
|
||||||
|
}
|
||||||
|
|
||||||
|
globals {
|
||||||
|
siteTitle = 'My Simple Site'
|
||||||
|
}
|
||||||
|
}
|
1
cli/src/main/resources/ssgBuildsBasic.groovy
Normal file
1
cli/src/main/resources/ssgBuildsBasic.groovy
Normal file
@ -0,0 +1 @@
|
|||||||
|
// This file was auto-generated by the ssg init command.
|
Loading…
Reference in New Issue
Block a user