/
cachr.plugin.coffee
171 lines (140 loc) · 3.91 KB
/
cachr.plugin.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
# Export Plugin
module.exports = (BasePlugin) ->
# Requires
balUtil = require('bal-util')
request = require('request')
path = require('path')
fs = require('fs')
# Define Plugin
class CachrPlugin extends BasePlugin
# Plugin Name
name: 'cachr'
# Default Configuration
config:
urlPrefix: '/_docpad/plugins/cachr'
pathPrefix: path.join '_docpad', 'plugins', 'cachr'
# URLs to Cache
urlsToCache: null # Object
urlsToCacheLength: 0
# -----------------------------
# Helpers
# Queue Remote Url Sync
# Mapped to templateData.cachr
# Takes a remote url and queues it for caching
queueRemoteUrlSync: (sourceUrl) ->
# Prepare
docpad = @docpad
config = @config
# Generate a path to return immediatly
name = path.basename(sourceUrl)
details =
name: name
sourceUrl: sourceUrl
cacheUrl: "#{config.urlPrefix}/#{name}"
cachePath: path.resolve(docpad.config.outPath, config.pathPrefix, name)
# Store it for saving later
@urlsToCache[sourceUrl] = details
@urlsToCacheLength++
# Return the cached url
return details.cacheUrl
# Save Remote Url
# Store a remote url
# next(err,details)
cacheRemoteUrl: (details,next) ->
# Prepare
docpad = @docpad
attempt = 1
# Get the file
viaRequest = ->
docpad.logger.log 'debug', "Cachr is fetching [#{details.sourceUrl}] to [#{details.cachePath}]"
# Fetch and Save
writeStream = fs.createWriteStream(details.cachePath)
request(
{
uri: details.sourceUrl
},
(err) ->
if err
++attempt
if attempt is 3
# give up, and delete out cachePath if it exists
path.exists details.cachePath, (exists) ->
if exists
fs.unlink details.cachePath, (err2) ->
return next?(err)
else
return next?(err)
else
return viaRequest() # try again
else
return next?() # success
).pipe(writeStream)
# Check if we should get the data from the cache or do a new request
balUtil.isPathOlderThan details.cachePath, 1000*60*5, (err,older) ->
# Check
return next?(err) if err
# The file doesn't exist, or exists and is old
if older is null or older is true
# Refresh
viaRequest()
# The file exists and relatively new
else
# So we don't care
next?()
# Chain
@
# -----------------------------
# Events
# Render Before
# Map the templateData functions
renderBefore: ({templateData}, next) ->
# Prepare
cachr = @
@urlsToCache = {}
@urlsToCacheLength = 0
# Apply
templateData.cachr = (sourceUrl) ->
return cachr.queueRemoteUrlSync(sourceUrl)
# Next
next?()
# Chain
@
# Write After
# Store all our files to be cached
writeAfter: ({templateData}, next) ->
# Prepare
cachr = @
docpad = @docpad
logger = @docpad.logger
config = @config
urlsToCache = @urlsToCache
urlsToCacheLength = @urlsToCacheLength
cachrPath = path.resolve(docpad.config.outPath, config.pathPrefix)
failures = 0
# Check
unless urlsToCacheLength
return next?()
# Log
logger.log 'info', 'Cachr started caching...', (if failures then "with #{failures} failures" else '')
# Ensure Path
balUtil.ensurePath cachrPath, (err) ->
# Check
return next?(err) if err
# Async
tasks = new balUtil.Group (err) =>
logger.log (if failures then 'warn' else 'info'), 'Cachr finished caching', (if failures then "with #{failures} failures" else '')
# Store all our files to be cached
balUtil.each urlsToCache, (details,sourceUrl) ->
tasks.push (complete) ->
cachr.cacheRemoteUrl details, (err) ->
if err
docpad.logger.log 'warn', "Cachr failed to fetch: #{sourceUrl}"
docpad.error(err)
++failures
return complete()
# Fire the tasks together
tasks.async()
# Continue with DocPad flow as we cache the files
return next?()
# Chain
@