index.js 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265
  1. var path = require('path')
  2. var fs = require('fs-extra')
  3. var terraform = require('candlewax')
  4. var async = require('async')
  5. var connect = require('connect')
  6. var mime = require('mime')
  7. var helpers = require('./helpers')
  8. var middleware = require('./middleware')
  9. var pkg = require('../package.json')
  10. /**
  11. * Server
  12. *
  13. * Host a single Harp application.
  14. *
  15. */
  16. exports.server = function(dirPath, options, callback){
  17. var app = connect()
  18. app.use(middleware.regProjectFinder(dirPath))
  19. app.use(middleware.setup)
  20. app.use(middleware.basicAuth)
  21. app.use(middleware.underscore)
  22. app.use(middleware.mwl)
  23. app.use(middleware.static)
  24. app.use(middleware.poly)
  25. app.use(middleware.process)
  26. app.use(middleware.fallback)
  27. return app.listen(options.port || 9966, options.ip, function(){
  28. app.projectPath = dirPath
  29. callback.apply(app, arguments)
  30. })
  31. }
  32. /**
  33. * Multihost
  34. *
  35. * Host multiple Harp applications.
  36. *
  37. */
  38. exports.multihost = function(dirPath, options, callback){
  39. var app = connect()
  40. app.use(middleware.notMultihostURL)
  41. app.use(middleware.index(dirPath))
  42. app.use(middleware.hostProjectFinder(dirPath))
  43. app.use(middleware.setup)
  44. app.use(middleware.basicAuth)
  45. app.use(middleware.underscore)
  46. app.use(middleware.mwl)
  47. app.use(middleware.static)
  48. app.use(middleware.poly)
  49. app.use(middleware.process)
  50. app.use(middleware.fallback)
  51. app.listen(options.port || 9000, callback)
  52. }
  53. /**
  54. * Mount
  55. *
  56. * Offer the asset pipeline as connect middleware
  57. *
  58. */
  59. exports.mount = function(mountPoint, root){
  60. if(!root){
  61. root = mountPoint
  62. mountPoint = null
  63. }else{
  64. var rx = new RegExp("^" + mountPoint)
  65. }
  66. var finder = middleware.regProjectFinder(root)
  67. return function(req, rsp, next){
  68. if(rx){
  69. if(!req.url.match(rx)) return next()
  70. var originalUrl = req.url
  71. req.url = req.url.replace(rx, "/")
  72. }
  73. finder(req, rsp, function(){
  74. middleware.setup(req, rsp, function(){
  75. middleware.static(req, rsp, function(){
  76. middleware.poly(req, rsp, function(){
  77. middleware.process(req, rsp, function(){
  78. if(originalUrl) req.url = originalUrl
  79. next()
  80. })
  81. })
  82. })
  83. })
  84. })
  85. }
  86. }
  87. /**
  88. * Pipeline
  89. *
  90. * Offer the asset pipeline as connect middleware
  91. *
  92. */
  93. exports.pipeline = function(root){
  94. console.log("Deprecated, please use MOUNT instead, this will be removed in a future version.");
  95. var publicPath = path.resolve(root)
  96. var terra = terraform.root(publicPath)
  97. return function(req, rsp, next){
  98. var normalizedPath = helpers.normalizeUrl(req.url)
  99. var priorityList = terraform.helpers.buildPriorityList(normalizedPath)
  100. var sourceFile = terraform.helpers.findFirstFile(publicPath, priorityList)
  101. if(!sourceFile) return next()
  102. terra.render(sourceFile, function(error, body){
  103. if(error) return next(error)
  104. if(!body) return next() // 404
  105. var outputType = terraform.helpers.outputType(sourceFile)
  106. var mimeType = helpers.mimeType(outputType)
  107. var charset = mime.charsets.lookup(mimeType)
  108. rsp.statusCode = 200
  109. rsp.setHeader('Content-Type', mimeType + (charset ? '; charset=' + charset : ''))
  110. rsp.setHeader('Content-Length', Buffer.byteLength(body, charset));
  111. rsp.end(body)
  112. })
  113. }
  114. }
  115. exports.pkg = pkg
  116. /**
  117. * Export middleware
  118. *
  119. * Make sure middleware is accessible
  120. * when using harp as a library
  121. *
  122. */
  123. exports.middleware = middleware;
  124. /**
  125. * Compile
  126. *
  127. * Compiles Single Harp Application.
  128. *
  129. */
  130. exports.compile = function(projectPath, outputPath, callback){
  131. /**
  132. * Both projectPath and outputPath are optional
  133. */
  134. if(!callback){
  135. callback = outputPath
  136. outputPath = "www"
  137. }
  138. if(!outputPath){
  139. outputPath = "www"
  140. }
  141. /**
  142. * Setup all the paths and collect all the data
  143. */
  144. try{
  145. outputPath = path.resolve(projectPath, outputPath)
  146. var setup = helpers.setup(projectPath, "production")
  147. var terra = terraform.root(setup.publicPath, setup.config.globals)
  148. }catch(err){
  149. return callback(err)
  150. }
  151. /**
  152. * Protect the user (as much as possible) from compiling up the tree
  153. * resulting in the project deleting its own source code.
  154. */
  155. if(!helpers.willAllow(projectPath, outputPath)){
  156. return callback({
  157. type: "Invalid Output Path",
  158. message: "Output path cannot be greater then one level up from project path and must be in directory starting with `_` (underscore).",
  159. projectPath: projectPath,
  160. outputPath: outputPath
  161. })
  162. }
  163. /**
  164. * Compile and save file
  165. */
  166. var compileFile = function(file, done){
  167. process.nextTick(function () {
  168. terra.render(file, function(error, body){
  169. if(error){
  170. done(error)
  171. }else{
  172. if(body){
  173. var dest = path.resolve(outputPath, terraform.helpers.outputPath(file))
  174. fs.mkdirp(path.dirname(dest), function(err){
  175. fs.writeFile(dest, body, done)
  176. })
  177. }else{
  178. done()
  179. }
  180. }
  181. })
  182. })
  183. }
  184. /**
  185. * Copy File
  186. *
  187. * TODO: reference ignore extensions from a terraform helper.
  188. */
  189. var copyFile = function(file, done){
  190. var ext = path.extname(file)
  191. if(!terraform.helpers.shouldIgnore(file) && [".jade", ".ejs", ".nunjucks", ".njk", ".md", ".styl", ".less", ".scss", ".sass", ".coffee"].indexOf(ext) === -1){
  192. var localPath = path.resolve(outputPath, file)
  193. fs.mkdirp(path.dirname(localPath), function(err){
  194. fs.copy(path.resolve(setup.publicPath, file), localPath, done)
  195. })
  196. }else{
  197. done()
  198. }
  199. }
  200. /**
  201. * Scan dir, Compile Less and Jade, Copy the others
  202. */
  203. helpers.prime(outputPath, { ignore: projectPath }, function(err){
  204. if(err) console.log(err)
  205. helpers.ls(setup.publicPath, function(err, results){
  206. async.each(results, compileFile, function(err){
  207. if(err){
  208. callback(err)
  209. }else{
  210. async.each(results, copyFile, function(err){
  211. setup.config['harp_version'] = pkg.version
  212. delete setup.config.globals
  213. callback(null, setup.config)
  214. })
  215. }
  216. })
  217. })
  218. })
  219. }