UNPKG

8.15 kBJavaScriptView Raw
1var path = require('path')
2var fs = require('fs-extra')
3var terraform = require('terraform')
4var async = require('async')
5var connect = require('connect')
6var mime = require('mime-types')
7var helpers = require('./helpers')
8var middleware = require('./middleware')
9var pkg = require('../package.json')
10var browserify = require('browserify')
11var watchify = require('watchify')
12var url = require("url")
13
14
15
16
17/**
18 * Server
19 *
20 * Host a single Harp application.
21 *
22 */
23
24exports.server = function(dirPath, options, callback){
25 var cjs = {}
26 var app = connect()
27 app.use(middleware.regProjectFinder(dirPath))
28 app.use(middleware.setup)
29 app.use(middleware.basicAuth)
30 app.use(middleware.underscore)
31
32 app.use(function(req, rsp, next){
33 var pathname = url.parse(req.url).pathname.replace(/^\/|\/$/g, '')
34 if (cjs.hasOwnProperty(pathname)){
35 rsp.statusCode = 200
36 rsp.end(cjs[pathname])
37 } else {
38 return next()
39 }
40 })
41
42 app.use(middleware.mwl)
43 app.use(middleware.static)
44 app.use(middleware.poly)
45 app.use(middleware.process)
46 app.use(middleware.fallback)
47
48 try {
49 var setup = helpers.setup(dirPath)
50 } catch(e) {
51 var setup = { publicPath: dirPath }
52 }
53
54
55 var b = browserify({
56 entries: [setup.publicPath + "/bundle.cjs"],
57 cache: {},
58 packageCache: {},
59 plugin: [watchify]
60 })
61
62 var update = function(info){
63 //b.bundle().pipe(fs.createWriteStream(dirPath + "/bundle.js"))
64 var chunks = []
65 var stream = b.bundle()
66
67 stream.on("data", function(chunk){
68 chunks.push(chunk)
69 })
70
71 stream.on("end", function(){
72 cjs["bundle.js"] = Buffer.concat(chunks)
73 })
74
75 }
76
77 b.on('update', update)
78
79 update()
80
81 // function bundle() {
82 // console.log(dirPath + "/bundle.cjs")
83 // b.bundle()
84 // //_stdout.write(util.format.apply(this, arguments) + '\n')
85 // //b.bundle().pipe(fs.createWriteStream('output.js'));
86 // }
87
88 return app.listen(options.port || 9966, options.ip, function(){
89 app.projectPath = dirPath
90 callback.apply(app, arguments)
91 })
92}
93
94
95/**
96 * Multihost
97 *
98 * Host multiple Harp applications.
99 *
100 */
101
102exports.multihost = function(dirPath, options, callback){
103 var app = connect()
104 app.use(middleware.notMultihostURL)
105 app.use(middleware.index(dirPath))
106 app.use(middleware.hostProjectFinder(dirPath))
107 app.use(middleware.setup)
108 app.use(middleware.basicAuth)
109 app.use(middleware.underscore)
110 app.use(middleware.mwl)
111 app.use(middleware.static)
112 app.use(middleware.poly)
113 app.use(middleware.process)
114 app.use(middleware.fallback)
115 app.listen(options.port || 9000, callback)
116}
117
118/**
119 * Mount
120 *
121 * Offer the asset pipeline as connect middleware
122 *
123 */
124
125exports.mount = function(mountPoint, root){
126
127 if(!root){
128 root = mountPoint
129 mountPoint = null
130 }else{
131 var rx = new RegExp("^" + mountPoint)
132 }
133
134 var finder = middleware.regProjectFinder(root)
135
136 return function(req, rsp, next){
137
138 if(rx){
139 if(!req.url.match(rx)) return next()
140 var originalUrl = req.url
141 req.url = req.url.replace(rx, "/")
142 }
143
144 finder(req, rsp, function(){
145 middleware.setup(req, rsp, function(){
146 middleware.static(req, rsp, function(){
147 middleware.poly(req, rsp, function(){
148 middleware.process(req, rsp, function(){
149 if(originalUrl) req.url = originalUrl
150 next()
151 })
152 })
153 })
154 })
155 })
156 }
157}
158
159
160/**
161 * Pipeline
162 *
163 * Offer the asset pipeline as connect middleware
164 *
165 */
166
167exports.pipeline = function(root){
168 console.log("Deprecated, please use MOUNT instead, this will be removed in a future version.");
169 var publicPath = path.resolve(root)
170 var terra = terraform.root(publicPath)
171
172 return function(req, rsp, next){
173 var normalizedPath = helpers.normalizeUrl(req.url)
174 var priorityList = terraform.helpers.buildPriorityList(normalizedPath)
175 var sourceFile = terraform.helpers.findFirstFile(publicPath, priorityList)
176
177 if(!sourceFile) return next()
178
179 terra.render(sourceFile, function(error, body){
180 if(error) return next(error)
181 if(!body) return next() // 404
182
183 var outputType = terraform.helpers.outputType(sourceFile)
184 var mimeType = helpers.mimeType(outputType)
185 var charset = mime.charsets.lookup(mimeType)
186 rsp.statusCode = 200
187 rsp.setHeader('Content-Type', mimeType + (charset ? '; charset=' + charset : ''))
188 rsp.setHeader('Content-Length', Buffer.byteLength(body, charset));
189 rsp.end(body)
190 })
191
192 }
193
194}
195
196exports.pkg = pkg
197
198/**
199 * Export middleware
200 *
201 * Make sure middleware is accessible
202 * when using harp as a library
203 *
204 */
205exports.middleware = middleware;
206
207/**
208 * Compile
209 *
210 * Compiles Single Harp Application.
211 *
212 */
213
214exports.compile = function(projectPath, outputPath, callback){
215
216 /**
217 * Both projectPath and outputPath are optional
218 */
219
220 if(!callback && typeof outputPath === "function"){
221 callback = outputPath
222 outputPath = "www"
223 }
224
225 if(!outputPath){
226 outputPath = "www"
227 }
228
229
230 /**
231 * Setup all the paths and collect all the data
232 */
233
234 try{
235 outputPath = path.resolve(projectPath, outputPath)
236 var setup = helpers.setup(projectPath, "production")
237 var terra = terraform.root(setup.publicPath, setup.config.globals)
238 }catch(err){
239 return callback(err)
240 }
241
242
243 /**
244 * Protect the user (as much as possible) from compiling up the tree
245 * resulting in the project deleting its own source code.
246 */
247
248 if(!helpers.willAllow(projectPath, outputPath)){
249 return callback({
250 type: "Invalid Output Path",
251 message: "Output path cannot be greater then one level up from project path and must be in directory starting with `_` (underscore).",
252 projectPath: projectPath,
253 outputPath: outputPath
254 })
255 }
256
257
258 /**
259 * Compile and save file
260 */
261
262 var compileFile = function(file, done){
263 process.nextTick(function () {
264 terra.render(file, function(error, body){
265 if(error){
266 done(error)
267 }else{
268 if(body){
269 var dest = path.resolve(outputPath, terraform.helpers.outputPath(file))
270 fs.mkdirp(path.dirname(dest), function(err){
271 fs.writeFile(dest, body, done)
272 })
273 }else{
274 if (file === "bundle.cjs"){
275
276 var b = browserify({
277 entries: [path.resolve(setup.publicPath, "bundle.cjs")],
278 cache: {},
279 packageCache: {},
280 plugin: []
281 })
282
283 var dest = path.resolve(outputPath, "bundle.js")
284 var chunks = []
285 var stream = b.bundle()
286
287 stream.on("data", function(chunk){
288 chunks.push(chunk)
289 })
290
291 stream.on("end", function(){
292 fs.writeFile(dest, Buffer.concat(chunks), done)
293 })
294
295 } else {
296 done()
297 }
298 }
299 }
300 })
301 })
302 }
303
304
305 /**
306 * Copy File
307 *
308 * TODO: reference ignore extensions from a terraform helper.
309 */
310 var copyFile = function(file, done){
311 var ext = path.extname(file)
312 if(!terraform.helpers.shouldIgnore(file) && [".jade", ".ejs", ".md", ".styl", ".less", ".scss", ".sass", ".coffee", ".cjs"].indexOf(ext) === -1){
313 var localPath = path.resolve(outputPath, file)
314 fs.mkdirp(path.dirname(localPath), function(err){
315 fs.copy(path.resolve(setup.publicPath, file), localPath, done)
316 })
317 }else{
318 done()
319 }
320 }
321
322 /**
323 * Scan dir, Compile Less and Jade, Copy the others
324 */
325
326 helpers.prime(outputPath, { ignore: projectPath }, function(err){
327 if(err) console.log(err)
328
329 helpers.ls(setup.publicPath, function(err, results){
330 async.each(results, compileFile, function(err){
331 if(err){
332 callback(err)
333 }else{
334 async.each(results, copyFile, function(err){
335 setup.config['harp_version'] = pkg.version
336 delete setup.config.globals
337 callback(null, setup.config)
338 })
339 }
340 })
341 })
342 })
343
344}