UNPKG

4.36 kBtext/coffeescriptView Raw
1
2async = require 'async'
3crypto = require 'crypto'
4pathutil = require 'path'
5zlib = require 'zlib'
6mime = require 'mime'
7
8# IE8 Compatibility
9mime.types.js = 'text/javascript'
10mime.extensions['text/javascript'] = 'js'
11
12{extend} = require './util'
13{EventEmitter} = require 'events'
14
15class exports.Asset extends EventEmitter
16 defaultMaxAge: 60*60*24*365 # one year
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 #@headers['content-length'] = @contents.length
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