pages
Version:
AngularJS / Node.js sidekick.
316 lines (185 loc) • 8.73 kB
text/coffeescript
program = require 'commander'
fs = require 'fs-extra'
path = require 'path'
refresh = require '../lib/refresh'
log = require '../lib/log'
server = require '../server'
utils = require '../lib/utils'
notify = require '../lib/notify'
generator = require '../lib/generator'
utils = require '../lib/utils'
bake = require "#{global.pkgBasePath}/tasks/bake"
configPath = "#{process.env.HOME}/.pages"
templatePath = "#{process.env.HOME}/.pages/templates"
depsPath = "#{process.env.HOME}/.pages/deps"
throwError = (error) ->
notify 'Pages Error', error
throw new Error error
setDev = () -> process.env.NODE_ENV = 'development'
setProd = () -> process.env.NODE_ENV = 'production'
setTest = () -> process.env.NODE_ENV = 'test'
createTemplateDir = (cb) -> utils.runWithCb "mkdir #{configPath}", () -> utils.runWithCb "mkdir #{templatePath}", () -> cb()
catchExceptions = () -> process.on 'uncaughtException', (error) -> log.err error
setPort = (opts) ->
if !opts.port then opts.port = 80 else opts.port = parseInt opts.port
process.env.PORT = opts.port
opts
sanitize = (text) -> text.replace(/^[\s,]+/,"").replace(/[\s,]+$/,"").replace(/\s*,+\s*(,+\s*)*/g,",").split(' ').join('-').toLowerCase()
camelCase = (text) -> text.replace(/^[\s,]+/,"").replace(/[\s,]+$/,"").replace(/\s*,+\s*(,+\s*)*/g,",").split(' ').join('-').toLowerCase()
isTemplate = (cb) -> fs.exists templatePath, cb
_todo_ This returns extremely slow if lots of files, just look for adequate file.
ifNotPagesApp = (cb) ->
try
require "#{process.cwd()}/pages"
throwError 'Already inside a Pages application, aborting.'
catch error
cb()
ifPagesApp = (cb) ->
try
require "#{process.cwd()}/pages"
cb()
catch error
throwError 'Invalid Pages application, aborting.'
taskRunner = (taskName, opts, cb) ->
try
task = require "#{process.cwd()}/tasks/#{taskName}"
task opts, () -> if cb then cb() else done()
catch error
Is task part of Pages?
try
task = require "#{global.pkgBasePath}/tasks/#{taskName}"
task opts, () -> if cb then cb() else done()
catch error
console.log error
throwError "No tasks found which match tasks/#{taskName}."
serverRunner = (opts) ->
opts = setPort opts
if opts.cached then global.cached = true else global.cached = false
Use compiled assets.
if opts.packaged
server.run () ->
refresh opts.browser
done "Pages application running on port #{process.env.PORT}."
Build the assets based on the environment.
else
taskRunner 'bake', opts, () -> server.run () ->
refresh opts.browser
done "Pages application running on port #{process.env.PORT}."
Generator validation.
validate = (opts) ->
throwError 'No type specified.' if !opts.type
throwError 'No name specified.' if !opts.name
types = [
'app'
'angular_module'
'page'
'server_module'
]
throwError 'Invalid type specified`.' if types.indexOf(opts.type) is -1
switch opts.type
when 'page'
throwError 'No route specified.' if !opts.route
return true
done = (msg) -> if msg then log.ok msg else log.ok 'Alright, alright.'
## Public API ##
program
.version JSON.parse(fs.readFileSync("#{global.pkgBasePath}/package.json")).version
### config ###
This command allows you to configure Pages.
program.command('config')
.description('Configure Pages.')
.option('--skeleton [skeleton]', 'Configure the default application skeleton by providing a local folder path.')
.option('--destroy-skeleton', 'Remove the configured application skeleton.')
.action (opts) ->
if opts.destroySkeleton
utils.runWithCb "rm -rf #{templatePath}", () -> done 'Removed configured application skeleton.'
return
isTemplate (template) ->
setDev()
throwError 'No skeleton specified.' if !opts.skeleton
Strip.
opts.skeleton = opts.skeleton.substring(0, opts.skeleton.length - 1) if opts.skeleton.substring(opts.skeleton.length - 1, opts.skeleton.length) is '/'
copyTemplate = () ->
utils.copyDir
from: opts.skeleton
to: templatePath
, done
if template
utils.runWithCb "rm -rf #{templatePath}", () -> createTemplateDir () -> copyTemplate()
else
createTemplateDir () -> copyTemplate()
### generate ###
This command allows us to generate a Pages application and its core pieces (as AngularJS modules).
program.command('generate')
.description('Genererate AngularJS applications, angular_modules, pages, and server_modules.')
.option('--type [type]', 'The component to generate, "app", "angular_module", "page", or "server_module".')
.option('--name [name]', 'The name of the generated component.')
.action (opts) ->
ifNotPagesApp () ->
isTemplate (template) ->
opts.template = template
setDev()
validate opts
switch opts.type
when 'app'
opts.APPNAME = sanitize opts.name
generator.app opts, done
when 'server_module'
opts.NAME = sanitize opts.name
generator.server_module opts, done
### task ###
This command allows us to invoke Pages Tasks.
program
.command('task [taskName]')
.description('Invoke Pages Tasks: "bake", "assets".')
.option('--env [env]', 'The environment to invoke the task in, `development` or `production` (if the Task does not set it).')
.action (taskName, opts) ->
ifPagesApp () ->
isTemplate (template) ->
opts.template = template
if opts.env and opts.env is 'production' then setProd() else setDev()
throwError 'No task specified.' if !taskName
taskRunner taskName, opts
### run ###
This command allows us to run a standard web server in `development` mode while also utilizing Pages Modules at both core and app levels.
program
.command('run')
.description('Run a Pages application in `development` mode.')
.option('--port [port]', 'The port to run the HTTP server on, defaults to `80`.')
.option('--browser [browser', 'The browser to reload, `chrome` or `safari`.')
.option('--packaged', 'The app has been `baked`, and this starts the server with pre-compiled assets`.')
.action (opts) ->
ifPagesApp () ->
setDev()
utils.runWithCb "mkdir #{configPath}", () -> utils.runWithCb "mkdir #{depsPath}", () ->
opts.watch = true
serverRunner opts
### start ###
This command allows us to run a standard web server in `production` mode while also utilizing Pages Modules at both core and app levels.
program
.command('start')
.description('Run a Pages application in `production` mode.')
.option('--port [port]', 'The port to run the HTTP server on, defaults to `80`.')
.option('--browser [browser', 'The browser to reload, `chrome` or `safari`.')
.option('--packaged', 'The app has been `baked`, and this starts the server with pre-compiled assets`.')
.action (opts) ->
ifPagesApp () ->
setProd()
catchExceptions()
opts.watch = false
serverRunner opts
### test ###
This command allows us to test both server and client code.
_todo_ Expose client tests.
program.command('test [file]')
.description('Run the tests inside the test/ or server_modules/test directory, or specify a file. ')
.option('--die', 'On a test fail, stop running tests.')
.option('--timeout [timeout]', 'How long each test should take, defaults to 10 seconds.')
.action (file, opts) ->
setTest()
if file then file = "./test/#{file}.coffee" else file = './test'
if opts.die then die = '-b' else die = ''
if opts.timeout then timeout = parseInt(opts.timeout) * 1000 else timeout = 10 * 1000
utils.runWithCb "#{global.pkgBasePath}/node_modules/mocha/bin/mocha --compilers coffee:coffee-script --require coffee-script #{file} --reporter spec --require should -t #{timeout} --colors -b", done
program.parse process.argv
program.help() unless program.args.length