-
-
Notifications
You must be signed in to change notification settings - Fork 241
/
docpad.coffee
executable file
·4820 lines (3876 loc) · 124 KB
/
docpad.coffee
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
# Requires
# Essential
pathUtil = require('path')
{lazyRequire} = require('lazy-require')
corePath = pathUtil.resolve(__dirname, '..', '..')
setImmediate = global?.setImmediate or process.nextTick # node 0.8 b/c
# Profile
if ('--profile' in process.argv)
# Debug
debugger
# Nodefly
if process.env.NODEFLY_KEY
console.log 'Loading profiling tool: nodefly'
lazyRequire 'nodefly', {cwd:corePath}, (err,nodefly) ->
return if err
nodefly.profile(process.env.NODEFLY_KEY, 'docpad')
console.log('Profiling with nodefly')
# Nodetime
if process.env.NODETIME_KEY
console.log 'Loading profiling tool: nodetime'
lazyRequire 'nodetime', {cwd:corePath}, (err,nodetime) ->
return if err
nodetime.profile({
accountKey: process.env.NODETIME_KEY
appName: 'DocPad'
})
console.log('Profiling with nodetime')
# Webkit Devtools
console.log 'Loading profiling tool: webkit-devtools-agent'
lazyRequire 'webkit-devtools-agent', {cwd:corePath}, (err) ->
return if err
console.log("Profiling with webkit-devtools-agent on process id:", process.pid)
# Necessary
_ = require('lodash')
CSON = require('cson')
balUtil = require('bal-util')
extendr = require('extendr')
eachr = require('eachr')
typeChecker = require('typechecker')
ambi = require('ambi')
{TaskGroup} = require('taskgroup')
safefs = require('safefs')
safeps = require('safeps')
ignorefs = require('ignorefs')
util = require('util')
superAgent = require('superagent')
{extractOptsAndCallback} = require('extract-opts')
{EventEmitterGrouped} = require('event-emitter-grouped')
# Base
{queryEngine,Backbone,Events,Model,Collection,View,QueryCollection} = require('./base')
# Utils
docpadUtil = require('./util')
# Models
FileModel = require('./models/file')
DocumentModel = require('./models/document')
# Collections
FilesCollection = require('./collections/files')
ElementsCollection = require('./collections/elements')
MetaCollection = require('./collections/meta')
ScriptsCollection = require('./collections/scripts')
StylesCollection = require('./collections/styles')
# Plugins
PluginLoader = require('./plugin-loader')
BasePlugin = require('./plugin')
# =====================================
# DocPad
# The DocPad Class
# Extends https://github.com/bevry/event-emitter-grouped
class DocPad extends EventEmitterGrouped
# =================================
# Variables
# ---------------------------------
# Modules
# Base
Events: Events
Model: Model
Collection: Collection
View: View
QueryCollection: QueryCollection
# Models
FileModel: FileModel
DocumentModel: DocumentModel
# Collections
FilesCollection: FilesCollection
ElementsCollection: ElementsCollection
MetaCollection: MetaCollection
ScriptsCollection: ScriptsCollection
StylesCollection: StylesCollection
# Plugins
PluginLoader: PluginLoader
BasePlugin: BasePlugin
# ---------------------------------
# DocPad
# DocPad's version number
version: null
getVersion: ->
@version ?= require(@packagePath).version
return @version
getVersionString: ->
if docpadUtil.isLocalDocPadExecutable()
return util.format(@getLocale().versionLocal, @getVersion(), @corePath)
else
return util.format(@getLocale().versionGlobal, @getVersion(), @corePath)
# Plugin version requirements
pluginVersion: '2'
# Process getters
getProcessPlatform: -> process.platform
getProcessVersion: -> process.version.replace(/^v/,'')
# The express and http server instances bound to docpad
serverExpress: null
serverHttp: null
getServer: (both=false) ->
{serverExpress,serverHttp} = @
if both
return {serverExpress, serverHttp}
else
return serverExpress
setServer: (servers) ->
# Apply
if servers.serverExpress and servers.serverHttp
@serverExpress = servers.serverExpress
@serverHttp = servers.serverHttp
# Cleanup
delete @config.serverHttp
delete @config.serverExpress
delete @config.server
destroyServer: ->
@serverHttp?.close()
@serverHttp = null
# @TODO figure out how to destroy the express server
# The caterpillar instances bound to docpad
loggerInstances: null
getLogger: -> @loggerInstances?.logger
getLoggers: -> @loggerInstances
setLoggers: (loggers) ->
if @loggerInstances
@warn('Loggers have already been set')
else
@loggerInstances = loggers
@loggerInstances.logger.setConfig(dry:true)
@loggerInstances.console.setConfig(dry:false).pipe(process.stdout)
return loggers
destroyLoggers: ->
if @loggerInstances
for own key,value of @loggerInstances
value.end()
@
# The action runner instance bound to docpad
actionRunnerInstance: null
getActionRunner: -> @actionRunnerInstance
action: (args...) => docpadUtil.action.apply(@, args)
# The error runner instance bound to docpad
errorRunnerInstance: null
getErrorRunner: -> @errorRunnerInstance
# The track runner instance bound to docpad
trackRunnerInstance: null
getTrackRunner: -> @trackRunnerInstance
# Event Listing
# Whenever a event is created, it must be applied here to be available to plugins and configuration files
# https://github.com/bevry/docpad/wiki/Events
events: [
'extendTemplateData' # fired each load
'extendCollections' # fired each load
'docpadLoaded' # fired multiple times, first time command line configuration hasn't been applied yet
'docpadReady' # fired only once
'docpadDestroy' # fired once on shutdown
'consoleSetup' # fired once
'generateBefore'
'populateCollectionsBefore'
'populateCollections'
'contextualizeBefore'
'contextualizeAfter'
'renderBefore'
'renderCollectionBefore'
'render' # fired for each extension conversion
'renderDocument' # fired for each document render, including layouts and render passes
'renderCollectionAfter'
'renderAfter'
'writeBefore'
'writeAfter'
'generateAfter'
'generated'
'serverBefore'
'serverExtend'
'serverAfter'
'notify'
]
getEvents: ->
@events
# ---------------------------------
# Collections
# Database collection
database: null # QueryEngine Collection
databaseTempCache: null
getDatabase: -> @database
getDatabaseSafe: -> @databaseTempCache or @database
destroyDatabase: ->
if @database?
@database.destroy()
@database = null
if @databaseTempCache?
@databaseTempCache.destroy()
@databaseTempCache = null
@
# Files by URL
# Used to speed up fetching
filesByUrl: null
# Files by Selector
# Used to speed up fetching
filesBySelector: null
# Files by Out Path
# Used to speed up conflict detection
# Do not use for anything else
filesByOutPath: null
# Blocks
blocks: null
### {
# A collection of meta elements
meta: null # Elements Collection
# A collection of script elements
scripts: null # Scripts Collection
# Collection of style elements
styles: null # Styles Collection
} ###
# Get a block
getBlock: (name,clone) ->
block = @blocks[name]
if clone
classname = name[0].toUpperCase()+name[1..]+'Collection'
block = new @[classname](block.models)
return block
# Set a block
setBlock: (name,value) ->
if @blocks[name]?
@blocks[name].destroy()
if value
@blocks[name] = value
else
delete @blocks[name]
else
@blocks[name] = value
@
# Get blocks
getBlocks: -> @blocks
# Set blocks
setBlocks: (blocks) ->
for own name,value of blocks
@setBlock(name,value)
@
# Each block
eachBlock: (fn) ->
eachr(@blocks, fn)
@
# Destroy Blocks
destroyBlocks: ->
if @blocks
for own name,block of @blocks
block.destroy()
@blocks[name] = null
@
# Collections
collections: null
# Get a collection
getCollection: (value) ->
if value
if typeof value is 'string'
if value is 'database'
return @getDatabase()
else
for collection in @collections
if value in [collection.options.name, collection.options.key]
return collection
else
for collection in @collections
if value is collection
return collection
return null
# Get a collection
destroyCollection: (value) ->
if value
if typeof value is 'string' and value isnt 'database'
@collections = @collections.filter (collection) ->
if value in [collection.options.name, collection.options.key]
collection?.destroy()
return false
else
return true
else if value isnt @getDatabase()
@collections = @collections.filter (collection) ->
if value is collection
collection?.destroy()
return false
else
return true
return null
# Add a collection
addCollection: (collection) ->
if collection and collection not in [@getDatabase(), @getCollection(collection)]
@collections.push(collection)
@
# Set a collection
# A collection can have multiple names
setCollection: (name, collection) ->
if collection
if name
collection.options.name = name
if @getCollection(name) isnt collection
@destroyCollection(name)
@addCollection(collection)
else
@destroyCollection(name)
# Get collections
getCollections: ->
return @collections
# Set collections
setCollections: (collections) ->
if Array.isArray(collections)
for value in collections
@addCollection(value)
else
for own name,value of collections
@setCollection(name, value)
@
# Each collection
eachCollection: (fn) ->
fn(@getDatabase(), 'database')
for collection,index in @collections
fn(collection, collection.options.name or collection.options.key or index)
@
# Destroy Collections
destroyCollections: ->
if @collections
for collection in @collections
collection.destroy()
@collections = []
@
# ---------------------------------
# Collection Helpers
# Get files (will use live collections)
getFiles: (query,sorting,paging) ->
key = JSON.stringify({query, sorting, paging})
collection = @getCollection(key)
unless collection
collection = @getDatabase().findAllLive(query, sorting, paging)
collection.options.key = key
@addCollection(collection)
return collection
# Get a single file based on a query
getFile: (query,sorting,paging) ->
file = @getDatabase().findOne(query, sorting, paging)
return file
# Get files at a path
getFilesAtPath: (path,sorting,paging) ->
query = $or: [{relativePath: $startsWith: path}, {fullPath: $startsWith: path}]
files = @getFiles(query, sorting, paging)
return files
# Get a file at a relative or absolute path or url
getFileAtPath: (path,sorting,paging) ->
file = @getDatabase().fuzzyFindOne(path, sorting, paging)
return file
# Get a file by its url
# TODO: Does this still work???
getFileByUrl: (url,opts={}) ->
opts.collection ?= @getDatabase()
file = opts.collection.get(@filesByUrl[url])
return file
# Get a file by its id
getFileById: (id,opts={}) ->
opts.collection ?= @getDatabase()
file = opts.collection.get(id)
return file
# Remove the query string from a url
# Pathname convention taken from document.location.pathname
getUrlPathname: (url) ->
return url.replace(/\?.*/,'')
# Get a file by its route
# next(err,file)
getFileByRoute: (url,next) ->
# Prepare
docpad = @
# If we have not performed a generation yet then wait until the initial generation has completed
if docpad.generated is false
# Wait until generation has completed and recall ourselves
docpad.once 'generated', ->
return docpad.getFileByRoute(url, next)
# hain
return @
# @TODO the above causes a signifcant delay when importing external documents (like tumblr data) into the database
# we need to figure out a better way of doing this
# perhaps it is via `writeSource: once` for imported documents
# or providing an option to disable this so it forward onto the static handler instead
# Prepare
database = docpad.getDatabaseSafe()
# Fetch
cleanUrl = docpad.getUrlPathname(url)
file = docpad.getFileByUrl(url, {collection:database}) or docpad.getFileByUrl(cleanUrl, {collection:database})
# Forward
next(null, file)
# Chain
@
# Get a file by its selector
# TODO: What on earth is a selector?
getFileBySelector: (selector,opts={}) ->
opts.collection ?= @getDatabase()
file = opts.collection.get(@filesBySelector[selector])
unless file
file = opts.collection.fuzzyFindOne(selector)
if file
@filesBySelector[selector] = file.id
return file
# ---------------------------------
# Skeletons
# Skeletons Collection
skeletonsCollection: null
# Get Skeletons
# Get all the available skeletons for us and their details
# next(err,skeletonsCollection)
getSkeletons: (next) ->
# Prepare
docpad = @
locale = @getLocale()
# Check if we have cached locally
if @skeletonsCollection?
return next(null,@skeletonsCollection)
# Fetch the skeletons from the exchange
@skeletonsCollection = new Collection()
@skeletonsCollection.comparator = queryEngine.generateComparator(position:1, name:1)
@getExchange (err,exchange) ->
# Check
return next(err) if err
# Prepare
index = 0
# If we have the exchange data, then add the skeletons from it
if exchange
for own skeletonKey,skeleton of exchange.skeletons
skeleton.id ?= skeletonKey
skeleton.name ?= skeletonKey
skeleton.position ?= index
docpad.skeletonsCollection.add(new Model(skeleton))
++index
# Add No Skeleton Option
docpad.skeletonsCollection.add(new Model(
id: 'none'
name: locale.skeletonNoneName
description: locale.skeletonNoneDescription
position: index
))
# Return Collection
return next(null, docpad.skeletonsCollection)
@
# ---------------------------------
# Plugins
# Plugins that are loading really slow
slowPlugins: null # {}
# Loaded plugins indexed by name
loadedPlugins: null # {}
# A listing of all the available extensions for DocPad
exchange: null # {}
# -----------------------------
# Paths
# The DocPad directory
corePath: corePath
# The DocPad library directory
libPath: __dirname
# The main DocPad file
mainPath: pathUtil.resolve(__dirname, 'docpad')
# The DocPad package.json path
packagePath: pathUtil.resolve(__dirname, '..', '..', 'package.json')
# The DocPad locale path
localePath: pathUtil.resolve(__dirname, '..', '..', 'locale')
# The DocPad debug log path
debugLogPath: pathUtil.join(process.cwd(), 'docpad-debug.log')
# The User's configuration path
userConfigPath: '.docpad.cson'
# -----------------------------
# Template Data
# DocPad's Template Data
initialTemplateData: null # {}
# Plugin's Extended Template Data
pluginsTemplateData: null # {}
# Get Complete Template Data
getTemplateData: (userTemplateData) ->
# Prepare
userTemplateData or= {}
docpad = @
{renderPasses} = @config
locale = @getLocale()
# Set the initial docpad template data
@initialTemplateData ?=
# Site Properties
site: {}
# Environment
getEnvironment: ->
return docpad.getEnvironment()
# Environments
getEnvironments: ->
return docpad.getEnvironments()
# Set that we reference other files
referencesOthers: (flag) ->
document = @getDocument()
document.referencesOthers()
return null
# Get the Document
getDocument: ->
return @documentModel
# Get a Path in respect to the current document
getPath: (path,parentPath) ->
document = @getDocument()
path = document.getPath(path, parentPath)
return path
# Get Files
getFiles: (query,sorting,paging) ->
@referencesOthers()
result = docpad.getFiles(query, sorting, paging)
return result
# Get another file's URL based on a relative path
getFile: (query,sorting,paging) ->
@referencesOthers()
result = docpad.getFile(query,sorting,paging)
return result
# Get Files At Path
getFilesAtPath: (path,sorting,paging) ->
@referencesOthers()
path = @getPath(path)
result = docpad.getFilesAtPath(path, sorting, paging)
return result
# Get another file's model based on a relative path
getFileAtPath: (relativePath) ->
@referencesOthers()
path = @getPath(relativePath)
result = docpad.getFileAtPath(path)
return result
# Get a specific file by its id
getFileById: (id) ->
@referencesOthers()
result = docpad.getFileById(id)
return result
# Get the entire database
getDatabase: ->
@referencesOthers()
return docpad.getDatabase()
# Get a pre-defined collection
getCollection: (name) ->
@referencesOthers()
return docpad.getCollection(name)
# Get a block
getBlock: (name) ->
return docpad.getBlock(name,true)
# Include another file taking in a relative path
include: (subRelativePath,strict=true) ->
file = @getFileAtPath(subRelativePath)
if file
if strict and file.get('rendered') is false
if renderPasses is 1
docpad.warn util.format(locale.renderedEarlyViaInclude, subRelativePath)
return null
return file.getOutContent()
else
err = new Error(util.format(locale.includeFailed, subRelativePath))
throw err
# Fetch our result template data
templateData = extendr.extend({}, @initialTemplateData, @pluginsTemplateData, @config.templateData, userTemplateData)
# Add site data
templateData.site.date or= new Date()
templateData.site.keywords or= []
if typeChecker.isString(templateData.site.keywords)
templateData.site.keywords = templateData.site.keywords.split(/,\s*/g)
# Return
templateData
# -----------------------------
# Locales
# Determined locale
locale: null
# Get Locale
getLocale: ->
if @locale? is false
config = @getConfig()
codes = _.uniq [
'en'
safeps.getLanguageCode config.localeCode
safeps.getLanguageCode safeps.getLocaleCode()
safeps.getLocaleCode config.localeCode
safeps.getLocaleCode safeps.getLocaleCode()
]
locales = (@loadLocale(code) for code in codes)
@locale = extendr.extend(locales...)
return @locale
# Load Locale
loadLocale: (code) ->
localePath = pathUtil.join(@localePath, "#{code}.cson")
return null unless safefs.existsSync(localePath)
locale = CSON.parseFileSync(localePath)
return locale
# -----------------------------
# Environments
# Get Environment
getEnvironment: ->
env = @getConfig().env or 'development'
return env
# Get Environments
getEnvironments: ->
env = @getEnvironment()
envs = env.split(/[, ]+/)
return envs
# -----------------------------
# Configuration
# Hash Key
# The key that we use to hash some data before sending it to our statistic server
hashKey: '7>9}$3hP86o,4=@T' # const
# Website Package Configuration
websitePackageConfig: null # {}
# Merged Configuration
# Merged in the order of:
# - initialConfig
# - userConfig
# - websiteConfig
# - instanceConfig
# - environmentConfig
config: null # {}
# Instance Configuration
instanceConfig: null # {}
# Website Configuration
websiteConfig: null # {}
# User Configuraiton
userConfig:
# Name
name: null
# Email
email: null
# Username
username: null
# Subscribed
subscribed: null
# Subcribe Try Again
# If our subscription has failed, when should we try again?
subscribeTryAgain: null
# Terms of Service
tos: null
# Identified
identified: null
# Initial Configuration
initialConfig:
# -----------------------------
# Plugins
# Force re-install of all plugin dependencies
force: false
# Whether or not we should use the global docpad instance
global: false
# Whether or not we should enable plugins that have not been listed or not
enableUnlistedPlugins: true
# Plugins which should be enabled or not pluginName: pluginEnabled
enabledPlugins: {}
# Whether or not we should skip unsupported plugins
skipUnsupportedPlugins: true
# Configuration to pass to any plugins pluginName: pluginConfiguration
plugins: {}
# Where to fetch the exchange information from
exchangeUrl: 'http://docpad.org/exchange.json'
# -----------------------------
# Project Paths
# The project directory
rootPath: process.cwd()
# The project's database cache path
databaseCachePath: '.docpad.db'
# The project's package.json path
packagePath: 'package.json'
# Where to get the latest package information from
latestPackageUrl: 'http://docpad.org/latest.json'
# The project's configuration paths
# Reads only the first one that exists
# If you want to read multiple configuration paths, then point it to a coffee|js file that requires
# the other paths you want and exports the merged config
configPaths: [
'docpad.js'
'docpad.coffee'
'docpad.json'
'docpad.cson'
]
# Plugin directories to load
pluginPaths: []
# The project's plugins directory
pluginsPaths: [
'node_modules'
'plugins'
]
# Paths that we should watch for reload changes in
reloadPaths: []
# Paths that we should watch for regeneration changes in
regeneratePaths: []
# The time to wait after a source file has changed before using it to regenerate
regenerateDelay: 100
# The time to wait before outputting the files we are waiting on
slowFilesDelay: 20*1000
# The project's out directory
outPath: 'out'
# The project's src directory
srcPath: 'src'
# The project's documents directories
# relative to the srcPath
documentsPaths: [
'documents'
'render'
]
# The project's files directories
# relative to the srcPath
filesPaths: [
'files'
'static'
'public'
]
# The project's layouts directory
# relative to the srcPath
layoutsPaths: [
'layouts'
]
# Ignored file patterns during directory parsing
ignorePaths: false
ignoreHiddenFiles: false
ignoreCommonPatterns: true
ignoreCustomPatterns: false
# Watch options
watchOptions: null
# -----------------------------
# Server
# Port
# The port that the server should use
# Defaults to these environment variables:
# - PORT — Heroku, Nodejitsu, Custom
# - VCAP_APP_PORT — AppFog
# - VMC_APP_PORT — CloudFoundry
port: null
# Hostname
# The hostname we wish to listen to
# Defaults to these environment variables:
# HOSTNAME — Generic
# Do not set to "localhost" it does not work on heroku
hostname: null
# Max Age
# The caching time limit that is sent to the client
maxAge: 86400000
# Server
# The Express.js server that we want docpad to use
serverExpress: null
# The HTTP server that we want docpad to use
serverHttp: null
# Extend Server
# Whether or not we should extend the server with extra middleware and routing
extendServer: true
# Which middlewares would you like us to activate
# The standard middlewares (bodyParser, methodOverride, express router)
middlewareStandard: true
# The standard bodyParser middleware
middlewareBodyParser: true
# The standard methodOverride middleware
middlewareMethodOverride: true
# The standard express router middleware
middlewareExpressRouter: true
# Our own 404 middleware
middleware404: true
# Our own 500 middleware
middleware500: true
# -----------------------------
# Logging
# Log Level
# Which level of logging should we actually output
logLevel: (if ('-d' in process.argv) then 7 else 6)
# Catch uncaught exceptions
catchExceptions: true
# Report Errors
# Whether or not we should report our errors back to DocPad
# By default it is only enabled if we are not running inside a test
reportErrors: process.argv.join('').indexOf('test') is -1
# Report Statistics
# Whether or not we should report statistics back to DocPad
# By default it is only enabled if we are not running inside a test
reportStatistics: process.argv.join('').indexOf('test') is -1
# -----------------------------
# Other
# Utilise the database cache
databaseCache: false # [false, true, 'write']
# Detect Encoding
# Should we attempt to auto detect the encoding of our files?
# Useful when you are using foreign encoding (e.g. GBK) for your files
detectEncoding: false
# Render Single Extensions
# Whether or not we should render single extensions by default
renderSingleExtensions: false
# Render Passes
# How many times should we render documents that reference other documents?
renderPasses: 1
# Offline
# Whether or not we should run in offline mode
# Offline will disable the following:
# - checkVersion
# - reportErrors
# - reportStatistics