123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377 |
- var fs = require('fs')
- var path = require('path')
- var mime = require('mime')
- var terraform = require('candlewax')
- var fse = require('fs-extra')
- var envy = require('envy-json')
- /**
- *
- * Normalize Url
- *
- * - removes querystring
- * - removes extra slashes
- * - changes `/` to `/index.html`
- */
- exports.normalizeUrl = function(url){
- // take off query string
- var base = unescape(url.split('?')[0])
- /**
- * Normalize Path
- *
- * Note: This converts unix paths to windows path on windows
- * (not sure if this is a good thing)
- */
- var file_path = path.normalize(base)
- // index.html support
- if (path.sep == file_path[file_path.length - 1]) file_path += 'index.html'
- return file_path
- }
- /**
- *
- * Mime Type
- *
- * returns type of the file
- *
- * TODO: reference ext from terraform
- */
- exports.mimeType = function(source){
- var ext = path.extname(source)
- if(['.jade', '.md', '.ejs'].indexOf(ext) !== -1){
- return mime.lookup('html')
- }else if(['.less', '.styl', '.scss', '.sass'].indexOf(ext) !== -1){
- return mime.lookup('css')
- } else if (['.js', '.coffee'].indexOf(ext) !== -1) {
- return mime.lookup('js')
- } else {
- return mime.lookup(source)
- }
- }
- /**
- *
- * Walk directory for files
- *
- * recursive function that returns the directory tree
- * http://stackoverflow.com/questions/5827612/node-js-fs-readdir-recursive-directory-search
- *
- */
- var walk = function(dir, done) {
- var results = []
- fs.readdir(dir, function(err, list) {
- if (err){
- return done(err)
- }
- var pending = list.length
- if (!pending) return done(null, results);
- list.forEach(function(file) {
- file = path.resolve(dir, file)
- fs.stat(file, function(err, stat) {
- if (stat && stat.isDirectory()) {
- walk(file, function(err, res) {
- results = results.concat(res)
- if (!--pending) done(null, results)
- })
- } else {
- results.push(file)
- if (!--pending) done(null, results)
- }
- })
- })
- })
- }
- /**
- *
- * Fetch all the file paths for a directory.
- * returns and array of all the relative paths.
- *
- */
- exports.ls = function(dir, callback) {
- walk(dir, function(err, results){
- var files = []
- results.map(function(file){ files.push(path.relative(dir, file)) })
- callback(null, files)
- })
- }
- /**
- * Setup
- *
- * This is the style and configuration of a Harp Application.
- * returns object with contents of Harp.json and application style
- *
- * {
- * "projectPath" : "/path/to/app",
- * "publicPath" : "/path/to/app/public",
- * "config" : { ... }
- * }
- */
- exports.setup = function(projectPath, env){
- if(!env) env = "development"
- try{
- var configPath = path.join(projectPath, "harp.json")
- var contents = fs.readFileSync(configPath).toString()
- var publicPath = path.join(projectPath, "public")
- }catch(e){
- try{
- var configPath = path.join(projectPath, "_harp.json")
- var contents = fs.readFileSync(configPath).toString()
- var publicPath = projectPath
- }catch(e){
- var contents = "{}"
- var publicPath = projectPath
- }
- }
- // not sure what this does anymore.
- if(!contents || contents.replace(/^\s\s*/, '').replace(/\s\s*$/, '') == ''){
- contents = '{}'
- }
- // attempt to parse the file
- try{
- var cfg = JSON.parse(contents)
- }catch(e){
- e.source = "JSON"
- e.dest = "CONFIG"
- e.message = e.message
- e.filename = configPath
- e.stack = contents
- e.lineno = -1
- throw new terraform.helpers.TerraformError(e)
- }
- if(!cfg.hasOwnProperty('globals')) cfg['globals'] = {}
- cfg.globals.environment = process.env.NODE_ENV || env
- // replace values that look like environment variables
- // e.g. '$foo' -> process.env.foo
- cfg = envy(cfg)
- return {
- projectPath : projectPath,
- publicPath : publicPath,
- config : cfg
- }
- }
- /**
- *
- * Template for outputing Less errors.
- *
- */
- exports.cssError = function(error){
- var body = '' +
- 'body{' +
- 'margin:0;' +
- '}' +
- 'body:before {' +
- 'display: block;'+
- 'white-space: pre;' +
- 'content: "'+ error.error.source +' -> ' + error.error.dest + ' (' + error.error.message + ') ' + error.error.filename + '";'+
- 'color: #444;'+
- 'background-color: #fefe96;' +
- 'padding: 40px 40px;'+
- 'margin: 0;'+
- 'font-family: monospace;'+
- 'font-size: 14px;'+
- '}'
- return body
- }
- /**
- *
- * Will Collide
- *
- * Returns true if first path is in the line of fire of the second path.
- * ie: if we delete the second path will the first path be affected?
- */
- var willCollide = exports.willCollide = function(projectPath, outputPath){
- var projectPath = path.resolve(projectPath)
- var outputPath = path.resolve(outputPath)
- var relativePath = path.relative(projectPath, outputPath)
- var arr = relativePath.split(path.sep)
- var result = true;
- arr.forEach(function(i){
- if(i !== "..") result = false
- })
- /**
- * for @kennethormandy ;)
- */
- if ([path.sep, "C:\\"].indexOf(outputPath) !== -1) result = true
- /**
- * For #400
- */
- if (projectPath === outputPath) result = true
- return result
- }
- /**
- *
- * Will Allow
- *
- * Returns `true` if we feel projectPath is safe from the output path.
- * For this to be the case. The outputPath must live only one directory
- * back from the projectPath and the projectPath must live in a directory
- * starting with an underscore.
- */
- exports.willAllow = function(projectPath, outputPath){
- var projectPath = path.resolve(projectPath)
- var outputPath = path.resolve(outputPath)
- var relativePath = path.relative(projectPath, outputPath)
- var arr = relativePath.split(path.sep)
- if(willCollide(projectPath, outputPath)){
- if(relativePath === ".."){
- if(projectPath.split(path.sep)[projectPath.split(path.sep).length - 1][0] == "_"){
- return true
- }else{
- return false
- }
- }else{
- return false
- }
- }else{
- return true
- }
- }
- /**
- * Prime
- * (Disk I/O)
- *
- * Cleans out a directory but ignores one (optionally).
- *
- * primePath: Absolute Path
- * options: Object
- * ignore: Absolute Path || Relative (to delete)
- *
- * This is a powerful Function so take it seriously.
- *
- */
- exports.prime = function(primePath, options, callback){
- if(!callback){
- callback = options
- options = {}
- }
- /**
- * Options (only one)
- */
- var ignorePath = options.ignore
- ? path.resolve(primePath, options.ignore)
- : null
- // Absolute paths are predictable.
- var primePath = path.resolve(primePath)
- fse.mkdirp(primePath, function(){
- fse.readdir(primePath, function(error, contents){
- /**
- * Delete each item in the directory in parallel. Thanks Ry!
- */
- if(contents.length == 0) return callback()
- var total = contents.length
- var count = 0
- contents.forEach(function(i){
- var filePath = path.resolve(primePath, i)
- var gitRegExp = new RegExp(/^.git/)
- /**
- * We leave `.git`, `.gitignore`, and project path.
- */
- if(filePath === ignorePath || i.match(gitRegExp)){
- count++
- if(count == total) callback()
- }else{
- fse.remove(filePath, function(err){
- count++
- if(count == total) callback()
- })
- }
- })
- })
- })
- }
- /**
- * Stacktrace
- *
- * Formats a stactrace
- *
- *
- * This is a powerful Function so take it seriously.
- *
- */
- exports.stacktrace = function(str, options){
- var lineno = options.lineno || -1
- var context = options.context || 8
- var context = context = context / 2
- var lines = ('\n' + str).split('\n')
- var start = Math.max(lineno - context, 1)
- var end = Math.min(lines.length, lineno + context)
- if(lineno === -1) end = lines.length
- var pad = end.toString().length
- var context = lines.slice(start, end).map(function(line, i){
- var curr = i + start
- return (curr == lineno ? ' > ' : ' ')
- + Array(pad - curr.toString().length + 1).join(' ')
- + curr
- + '| '
- + line
- }).join('\n')
- return context
- }
|