Hack to quickly make apps using a single coffeescript source
npm install reputilHack to quickly make apps using a single coffeescript source
- 0.0.4 Making it work without coffee installed (ie. on ci-servers). Autocompile bugfix.
- 0.0.3 Fix autocompile for multiple files. Make reputil.coffee the executable.
- 0.0.2 build action which does all the usual stuff: compile, generate readme, etc.
- 0.0.1
- action: genreadme automatically generate README.md from package.json and literate coffeescript,
- action: autocompile autorestart coffee -wc
- reputil own infrastructure: git-hook, npm prepublish build binary etc.
- package.json
- call reputil from prepublish
- autoinclude reputil in devDependencies
- bower.json
- autogen as much as possible from package.json, quit if bower.json is present, but misses data
- git commit-hook npm prepublish
- dist with increment version in package, tag, npm publish, bower publish
This is a quick hack...
Modules
fs = require "fs"
glob = require "glob"
child_process = require "child_process"
package.json, and bower.json for the repository we are working on
pkg = undefined
cfg = undefined
bower = undefined
action dispatch
actions = {}
deepExtend = (target, src) ->
return if !src
for key, val of src
if typeof val == "object" && typeof target[key] == "object"
deepExtend target[key], val
else
target[key] = val
exec = (cmd, fn) ->
console.log "> #{cmd}"
child = child_process.exec cmd
child.stdout.pipe process.stdout
child.stderr.pipe process.stderr
child.on "exit", (result) -> fn?(result)
actions.build = ->
actions.compile()
actions.genreadme()
actions.genbower()
actions.genconfigxml()
actions.genManifestWebapp()
actions.genpackage = ->
try
pkg = fs.readFileSync "package.json"
catch e
pkg = "{}"
pkg = JSON.parse pkg
pkg.version ?= "0.0.0"
pkg.version = cfg.version if cfg.version
pkg.name = cfg.name
pkg.description = cfg.desc
pkg.license = cfg.license
pkg.keywords = cfg.tags
pkg.authors = [cfg.author]
pkg.repository =
type: "git"
url: "https://github.com/#{cfg.repos}.git"
deepExtend pkg, cfg.package
fs.writeFileSync "package.json", JSON.stringify(pkg, null, 2) + "\n"
actions.genbower = ->
try
bower = JSON.parse fs.readFileSync "bower.json"
catch e
bower = {}
bower.name = pkg.name
bower.version = pkg.version
bower.description = pkg.description
bower.license = pkg.license
bower.keywords = pkg.keywords
bower.authors = pkg.author
bower.repository = pkg.repository
deepExtend bower, cfg.bower
fs.writeFileSync "bower.json", JSON.stringify(bower, null, 2) + "\n"
actions.genManifestWebapp = ->
return if !cfg.phonegap
exec "convert -resize 128x128 icon.png icon128.png"
basedir = cfg.phonegap.basedir || "/#{cfg.name}/"
webapp =
version: cfg.version
name: cfg.title
description: cfg.desc
launch_path: "#{basedir}index.html"
icons:
"512": "#{basedir}icon.png"
"128": "#{basedir}icon128.png"
developer:
name: cfg.author
url: "http://#{cfg.site}"
appcache_path: "#{basedir}cache.manifest"
webapp.orientation = [cfg.phonegap.orientation] if cfg.phonegap.orientation
webapp.fullscreen = true if cfg.phonegap.fullscreen
fs.writeFileSync "manifest.webapp", JSON.stringify webapp
actions.genconfigxml = ->
return if !cfg.phonegap
fs.writeFileSync "config.xml", """
id = "#{cfg.site.split(".").reverse().join(".")}.#{cfg.name}"
version = "#{pkg.version}">
{("
{if cfg.phonegap.splash then '
\n"""
actions.genhtml = ->
return if !cfg.html
viewport = cfg.html.viewport || "width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=0"
css = cfg.html.css || []
js = cfg.html.js || []
fnames = []
for module, _ of bower.dependencies || {}
if !(module in (cfg.html.exclude || []))
moduleMain = (JSON.parse fs.readFileSync "bower_components/#{module}/bower.json").main
moduleMain = [moduleMain] if !Array.isArray moduleMain
for file in moduleMain
fname = "bower_components/#{module}/#{file}"
css.push fname if file.match /\.css$/
js.push fname if file.match /\.js$/
fnames.push fname
fnames = fnames.concat cfg.files if cfg.files
exec "git add -f #{fnames.join " "}"
js.push cfg.src.replace /.coffee$/, ".js"
fnames.push cfg.src.replace /.coffee$/, ".js"
actualHtml = (opt) ->
opt ?= {}
"""
actions.gencoffee = ->
return if fs.existsSync cfg.src
fs.writeFileSync cfg.src, """
\# {\{{1 Boilerplate
\# predicates that can be optimised away by uglifyjs
if typeof isNodeJs == "undefined" or typeof runTest == "undefined" then do ->
root = if typeof window == "undefined" then global else window
root.isNodeJs = (typeof process != "undefined") if typeof isNodeJs == "undefined"
root.isWindow = (typeof window != "undefined") if typeof isWindow == "undefined"
root.isPhoneGap = typeof document?.ondeviceready != "undefined" if typeof isPhoneGap == "undefined"
root.runTest = (if isNodeJs then process.argv[2] == "test" else location.hash.slice(1) == "test") if typeof runTest == "undefined"
\# use - require/window.global with non-require name to avoid being processed in firefox plugins
use = if isNodeJs then ((module) -> require module) else ((module) -> window[module])
\# execute main
onReady = (fn) ->
if isWindow
if document.readystate != "complete" then fn() else setTimeout (-> onReady fn), 17
\# {\{{1 Actual code
onReady ->
console.log "HERE"
\n"""
actions.genreadme = ->
source = fs.readFileSync cfg.src, "utf-8"
readme = "# #{cfg.title} #{pkg.version}\n\n"
readme += pkg.description || ""
readme += "\n"
if fs.existsSync ".travis.yml"
readme += "\n\n"
if cfg.testling
readme += "\n\n"
for line in source.split("\n")
continue if line.trim() in ["#!/usr/bin/env coffee"]
if (line.search /^\s*#/) == -1
line = " " + line
isCode = true
else
line = line.replace /^\s*# ?/, ""
line = line.replace new RegExp("(.){{" + "{(\\d)(.)"), (_, a, header, b) ->
("#" for i in [1..+header]).join("") + " " + (a + b).trim()
isCode = false
if isCode != prevWasCode
readme += "\n"
prevWasCode = isCode
readme += line + "\n"
readme += "\n----\n\nREADME.md autogenerated from #{cfg.src} "
readme += "!solsort\n"
fs.writeFileSync "README.md", readme
When using vim, coffee -wc sometimes exit when new version is saved (due to vims way of saving). This action keeps running coffee -wc on the files in the directory.
actions.autocompile = ->
spawnChild = (fname) ->
cmd = "#{__dirname}/node_modules/.bin/coffee -wc #{fname}"
console.log cmd
child = child_process.exec cmd
child.stdout.pipe process.stdout
child.stderr.pipe process.stderr
child.on "exit", -> spawnChild fname
spawnChild cfg.src
actions.compile = ->
child = child_process.exec "#{__dirname}/node_modules/.bin/coffee -c #{cfg.src}"
child.stdout.pipe process.stdout
child.stderr.pipe process.stderr
if !actions[process.argv[2]]
console.log "usage: reputil #{Object.keys(actions).join "|"}"
process.exit 1
try
cfg = (require "js-yaml").safeLoad fs.readFileSync "about.yml", "utf-8"
catch e
console.log e
console.log "Could not find/read/parse \"about.yml\" in current directory."
process.exit 1
throw "about.yml missing name" if !cfg.name
cfg.title ?= cfg.name
cfg.repos ?= "#{process.env.USER}/#{cfg.name}"
cfg.src ?= cfg.name + ".coffee"
cfg.author ?= process.env.USER
cfg.site ?= "#{process.env.USER}.username"
if cfg.author == "rasmuserik"
cfg.author = "Rasmus Erik Voel Jensen (solsort.com)"
cfg.site= "solsort.com"
actions.genpackage()
actions.genbower()
actions.gencoffee()
actions.genhtml()
actions[process.argv[2]]()
----
README.md autogenerated from reputil.coffee !solsort