Skip to content
This repository has been archived by the owner on Nov 9, 2017. It is now read-only.

Commit

Permalink
First draft
Browse files Browse the repository at this point in the history
  • Loading branch information
EtienneLem committed Jun 28, 2012
1 parent 08ff3c0 commit 66e2b9b
Show file tree
Hide file tree
Showing 7 changed files with 396 additions and 0 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Original file line Diff line number Diff line change
@@ -0,0 +1 @@
node_modules/
10 changes: 10 additions & 0 deletions bin/skeleton
Original file line number Original file line Diff line number Diff line change
@@ -0,0 +1,10 @@
#!/usr/bin/env node

var path = require('path')
var fs = require('fs')
var lib = path.join(path.dirname(fs.realpathSync(__filename)), '../lib')

require('coffee-script')

Skeleton = require(lib + '/skeleton')
new Skeleton
2 changes: 2 additions & 0 deletions lib/skeleton/helpers.coffee
Original file line number Original file line Diff line number Diff line change
@@ -0,0 +1,2 @@
Array::last = ->
this[this.length - 1]
95 changes: 95 additions & 0 deletions lib/skeleton/index.coffee
Original file line number Original file line Diff line number Diff line change
@@ -0,0 +1,95 @@
# Node.js dependencies
fs = require 'fs'
path = require 'path'
util = require 'util'
mkdirp = require 'mkdirp'

# Local dependencies
Template = require './template'
OptionParser = require './option_parser'

# Helpers
require './helpers'

# Main class
class Skeleton

@VERSION = '0.0.1'

@OPTIONS = [
['-h', '--help', 'display this help message']
['-v', '--version', 'display the version number']
['-r', '--renderer', 'use specified renderer [only ejs for now]']
]

# Bin command
constructor: ->
args = process.argv.splice(2)
options = new OptionParser(args)

if options.help
this.displayHelp()
return

if options.version
this.displayVersion()
return

this.createProject(options.appName, options) if options.appName

createProject: (appName, opts) =>
template = new Template(appName, opts)
for filename, content of template.files
this.write filename, "#{content}\n"

write: (path, content) ->
this.mkdir path, =>
return if path.split('/').pop() == 'empty'

fs.writeFile path, content, (err) =>
throw err if err
this.displayLine "=> Create #{path}"

mkdir: (filename, callback=null) ->
parts = filename.split('/')
parts.pop()
path = parts.join('/')

mkdirp path, '0755', (err) ->
throw err if err
callback() if callback

# Display messages
displayHelp: ->
rules = []
longest = 0

for option in Skeleton.OPTIONS
short = option[0]
long = option[1]
desc = option[2]

length = short.length + long.length
longest = length if length > longest

rules.push
short: short
long: long
desc: desc
length: length

this.displayLine '\nUsage: skeleton [options] myapp\n'

for rule in rules
spaces = new Array(longest - rule.length + 3).join(' ')
this.displayLine "#{rule.short}, #{rule.long}#{spaces}#{rule.desc}"

displayVersion: ->
this.displayLine "Skeleton version #{Skeleton.VERSION}"

displayLine: (line) ->
process.stdout.write "#{line}\n"


# Exports
module.exports = Skeleton
25 changes: 25 additions & 0 deletions lib/skeleton/option_parser.coffee
Original file line number Original file line Diff line number Diff line change
@@ -0,0 +1,25 @@
class OptionParser

constructor: (@args) ->
joinedArgs = @args.join('|')

# TODO: Make the returned object completely dynamic
return {
help: joinedArgs.search(/-h|--help/) > -1 || args.length == 0
version: joinedArgs.search(/-v|--version/) > -1
renderer: this.getOptionValue ['-r', '--renderer']
appName: this.getOptionValue(['-a', '--appname']) || @args.last()
}

# Private
# Returns given flags value or null
getOptionValue: (flags) ->
for flag in flags
index = @args.indexOf(flag)
return @args[index + 1] if index > -1

null


# Exports
module.exports = OptionParser
246 changes: 246 additions & 0 deletions lib/skeleton/template.coffee
Original file line number Original file line Diff line number Diff line change
@@ -0,0 +1,246 @@
class Template

constructor: (@appName, @opts) ->
@files = this.setFiles()

setFiles: ->
files = {}

# ./myapp
files["#{@appName}/.gitignore"] = """
node_modules/
"""

files["#{@appName}/package.json"] = """
{
"name": "#{@appName}"
, "version": "0.0.1"
, "dependencies": {
"express": "3.0.x"
, "connect-assets": "2.1.x"
, "stylus": "*"
, "ejs": "*"
, "coffee-script": "*"
}
, "scripts": {
"start": "server.js"
}
, "engines": {
"node": "0.8.0"
}
}
"""

files["#{@appName}/README.md"] = """
# #{@appName}
***
App structure generated by [Skeleton](https://github.com/EtienneLem/skeleton)
"""

files["#{@appName}/server.js"] = """
require("coffee-script")
require("./app/app.coffee")
"""

# ./myapp/app
files["#{@appName}/app/app.coffee"] = """
# Modules
express = require 'express'
http = require 'http'
app = express()
# Boot setup
require("\#{__dirname}/../config/boot")(app)
# Configuration
app.configure ->
port = process.env.PORT || 3000
if process.argv.indexOf('-p') >= 0
port = process.argv[process.argv.indexOf('-p') + 1]
app.set 'port', port
app.set 'views', "\#{__dirname}/views"
app.set 'view engine', 'ejs'
app.use express.static("\#{__dirname}/../public")
app.use express.favicon()
app.use express.logger('dev')
app.use express.bodyParser()
app.use express.methodOverride()
app.use require('connect-assets')(src: "\#{__dirname}/assets")
app.use app.router
app.configure 'development', ->
app.use express.errorHandler()
# Routes
require("\#{__dirname}/routes")(app)
# Server
http.createServer(app).listen app.get('port'), ->
console.log "Express server listening on port \#{app.get 'port'} in \#{app.settings.env} mode"
"""

# ./myapp/app/assets/css
files["#{@appName}/app/assets/css/styles.styl"] = """
// Based on <https://github.com/heliom/stylus-utils/blob/master/styles.styl-sample>
// @import "nib"
// Reset ---------------------------------------------------------------------
*
margin: 0; padding: 0
-webkit-box-sizing: border-box
-moz-box-sizing: border-box
box-sizing: border-box
// Base ----------------------------------------------------------------------
html
font-size: 62.5%
height: 100%
body
font-size: 16
body, legend, input, textarea, button
font-family: 'Helvetica Neue'
line-height: 1.4
color: #333
a:link, a:visited { color: deeppink }
a:focus, a:link:hover { color: hotpink }
"""

# ./myapp/app/assets/js
files["#{@appName}/app/assets/js/scripts.coffee"] = """
# console.log '#{@appName}'
"""

# ./myapp/app/controllers
files["#{@appName}/app/controllers/application_controller.coffee"] = """
class ApplicationController
# GET /
@index = (req, res) ->
res.render 'index',
view: 'index'
# Exports
module.exports = (app) ->
app.ApplicationController = ApplicationController
"""

# ./myapp/app/helpers
files["#{@appName}/app/helpers/index.coffee"] = """
fs = require 'fs'
# Recursively require a folder’s files
exports.autoload = autoload = (dir, app) ->
fs.readdirSync(dir).forEach (file) ->
path = "\#{dir}/\#{file}"
stats = fs.lstatSync(path)
# Go through the loop again if it is a directory
if stats.isDirectory()
autoload path, app
else
require(path)?(app)
# Capitalize a string
# string => String
String::capitalize = () ->
this.replace /(?:^|s)S/g, (a) -> a.toUpperCase()
# Classify a string
# application_controller => ApplicationController
String::classify = (str) ->
classified = []
words = str.split('_')
for word in words
classified.push word.capitalize()
classified.join('')
"""

# ./myapp/app/routes
files["#{@appName}/app/routes/index.coffee"] = """
module.exports = (app) ->
# Index
app.get '/', app.ApplicationController.index
# Error handling (No previous route found. Assuming it’s a 404)
app.get '/*', (req, res) ->
NotFound res
NotFound = (res) ->
res.render '404', status: 404, view: 'four-o-four'
"""

# ./myapp/app/views
files["#{@appName}/app/views/404.ejs"] = "<h1>Nothing here…</h1>"

files["#{@appName}/app/views/index.ejs"] = """
<h1>This page has been generated by <a href="https://github.com/EtienneLem/skeleton">Skeleton</a></h1>
<p>Edit in <span>#{@appName}/views/index.ejs</span></p>
"""

files["#{@appName}/app/views/layout.ejs"] = """
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>#{@appName}</title>
<%- css('styles.css') %>
</head>
<body data-view="<%= view %>">
<%- body %>
<script>
document.write('<script src=' +
('__proto__' in {} ? 'http://zeptojs.com/zepto.min.js' : 'http://code.jquery.com/jquery-1.7.2.min.js') +
'.js><\/script>')
</script>
<%- js('scripts.js') %>
</body>
</html>
"""

# ./myapp/config
files["#{@appName}/config/boot.coffee"] = """
module.exports = (app) ->
# Helpers
app.helpers = require "\#{__dirname}/../app/helpers"
# Lib
app.helpers.autoload "\#{__dirname}/../lib", app
# Controllers
app.helpers.autoload "\#{__dirname}/../app/controllers", app
"""

# ./myapp/lib/myapp
files["#{@appName}/lib/#{@appName}/my_custom_class.coffee"] = """
# module.exports = (app) ->
# # Your code
#
#
# Or if you want this to be a class
#
# class MyCustomClass
#
# constructor: (args) ->
# # Your code
#
# # Exports
# module.exports = (app) ->
# app.MyCustomClass = MyCustomClass
#
# Usage: new app.MyCustomClass(args)
"""

# ./myapp/public
files["#{@appName}/public/empty"] = ""

# Return the files object
files


module.exports = Template
Loading

0 comments on commit 66e2b9b

Please sign in to comment.