1 |
|
2 | async = require 'async'
|
3 | crypto = require 'crypto'
|
4 | pathutil = require 'path'
|
5 | zlib = require 'zlib'
|
6 | mime = require 'mime'
|
7 |
|
8 |
|
9 | mime.types.js = 'text/javascript'
|
10 | mime.extensions['text/javascript'] = 'js'
|
11 |
|
12 | {extend} = require './util'
|
13 | {EventEmitter} = require 'events'
|
14 |
|
15 | class exports.Asset extends EventEmitter
|
16 | defaultMaxAge: 60*60*24*365
|
17 | constructor: (options) ->
|
18 | super()
|
19 | options ?= {}
|
20 | @url = options.url if options.url?
|
21 | @contents = options.contents if options.contents?
|
22 | @headers = if options.headers then options.headers else {}
|
23 | headers = {}
|
24 | for key, value of @headers
|
25 | headers[key.toLowerCase()] = value
|
26 | @headers = headers
|
27 | @ext = pathutil.extname @url
|
28 | @mimetype = options.mimetype if options.mimetype?
|
29 | @mimetype ?= mime.types[@ext.slice(1, @ext.length)]
|
30 | @mimetype ?= 'text/plain'
|
31 | @gzip = options.gzip
|
32 | @hash = options.hash if options.hash?
|
33 | @maxAge = options.maxAge if options.maxAge?
|
34 | @allowNoHashCache = options.allowNoHashCache if options.allowNoHashCache?
|
35 | @on 'newListener', (event, listener) =>
|
36 | if event is 'complete' and @completed is true
|
37 | listener()
|
38 | @on 'created', (data) =>
|
39 | if data?.contents?
|
40 | @contents = data.contents
|
41 | if data?.assets?
|
42 | @assets = data.assets
|
43 | if @contents?
|
44 | @createSpecificUrl()
|
45 | @createHeaders()
|
46 | @completed = true
|
47 | @emit 'complete'
|
48 | @on 'error', (error) =>
|
49 | throw error if @listeners 'error' is 1
|
50 | @on 'start', =>
|
51 | @maxAge ?= @rack?.maxAge
|
52 | @maxAge ?= @defaultMaxAge unless @hash is false
|
53 | @allowNoHashCache ?= @rack?.allowNoHashCache
|
54 | @create options
|
55 | process.nextTick =>
|
56 | @maxAge ?= @defaultMaxAge
|
57 | return @create options unless @rack?
|
58 |
|
59 | respond: (request, response) ->
|
60 | headers = {}
|
61 | if request.url is @url and @allowNoHashCache isnt true
|
62 | for key, value of @headers
|
63 | headers[key] = value
|
64 | delete headers['cache-control']
|
65 | else
|
66 | headers = @headers
|
67 | for key, value of headers
|
68 | response.header key, value
|
69 | if @gzip
|
70 | response.send @gzipContents
|
71 | else response.send @contents
|
72 |
|
73 | checkUrl: (url) ->
|
74 | url is @specificUrl or (not @hash? and url is @url)
|
75 |
|
76 | handle: (request, response, next) ->
|
77 | handle = =>
|
78 | if @assets?
|
79 | for asset in @assets
|
80 | if asset.checkUrl request.url
|
81 | return asset.respond request, response
|
82 | if @checkUrl(request.url)
|
83 | @respond request, response
|
84 | else next()
|
85 | if @completed is true
|
86 | handle()
|
87 | else @on 'complete', ->
|
88 | handle()
|
89 |
|
90 | create: (options) ->
|
91 | @emit 'created'
|
92 |
|
93 | createHeaders: ->
|
94 | @headers['content-type'] ?= "#{@mimetype}"
|
95 | if @gzip
|
96 | @headers['content-encoding'] ?= 'gzip'
|
97 |
|
98 | if @maxAge?
|
99 | @headers['cache-control'] ?= "public, max-age=#{@maxAge}"
|
100 |
|
101 | tag: ->
|
102 | switch @mimetype
|
103 | when 'text/javascript'
|
104 | tag = "\n<script type=\"#{@mimetype}\" "
|
105 | return tag += "src=\"#{@specificUrl}\"></script>"
|
106 | when 'text/css'
|
107 | return "\n<link rel=\"stylesheet\" href=\"#{@specificUrl}\">"
|
108 |
|
109 | createSpecificUrl: ->
|
110 | @md5 = crypto.createHash('md5').update(@contents).digest 'hex'
|
111 | if @hash is false
|
112 | @useDefaultMaxAge = false
|
113 | return @specificUrl = @url
|
114 | @specificUrl = "#{@url.slice(0, @url.length - @ext.length)}-#{@md5}#{@ext}"
|
115 | if @hostname?
|
116 | @specificUrl = "//#{@hostname}#{@specificUrl}"
|
117 | if @gzip
|
118 | zlib.gzip @contents, (error, gzip) =>
|
119 | @gzipContents = gzip
|
120 |
|
121 | isRelevantUrl: (specificUrl) ->
|
122 | baseUrl = @url.slice(0, @url.length - @ext.length)
|
123 | if specificUrl.indexOf baseUrl isnt -1 and @ext is pathutil.extname specificUrl
|
124 | return true
|
125 | return false
|
126 |
|
127 | @extend: extend
|