static site builder + single page app helper

= |ˆڡˆ|ᕗ

== squareboy

== Source

$ git clone git@github.com:harsha-mudi/squareboy.git


~~ 400 lines

== Install

npm install squareboy

== About

squareboy removes the tedious aspects of web-dev. +

Each website/webapp is represented by a .tasklist.coffee file. +

It comes with a websocket/proxy/static server. +

Also, you never have to deal with bower.json or package.json with squareboy.

You can use squareboy as a build server which sends out websocket notifications for live reloading for example. +

== Running

squareboy foobar.tasklist.coffee --key value ...

A tasklist file is watched. + squareboy restarts the process, when it changes. + Manual restart is triggerred with "Enter" keypress on your terminal +

== A Blog Tasklist

require "squareboy"

comment following below will describe a squareboy export

gulp knowledge is assumed

squareboy exports globals

each global may export another global instead returning

source, dist are provided for folder management

source folder: "lexical" dist folder: "/Users/harsha/foobar/lexical"

those give out $source, $dist globals

which can take a dir or a file

for e in ["css", "fonts", "images","data"] $source.dir(e) $dist.dir(e)

for e in ["contents", "templates"] $source.dir(e)

file adds folder + first-arg

$source.file("appfile", "app/main.js")

abs_file doesn't add it

$source.abs_file("bundlefile", "bundle.js") $source.file("lessfile", "css/styles.less")

last argument is a glob pattern

$dist.abs_dir("vendor", "vendor", null); $dist.dir("js"); $dist.dir("tags"); $dist.abs_dir("html", $dist.folder, "*.html");

$args is command line options parsed by yargs

opts = min : (false || $args.min)

never deal with bower.json or package.json or

their command-line counterparts directly

npm "gulp-less" npm "gulp-rename" npm "gulp-minify-css" npm "gulp-size"

this gives you $less, $rename ...

$ is short for gulp

from for src

to for dest

log for logging

$if is gulp-if

__ is for ignoring errors with plumber

$.task "css", -> # File checking helper exists $source.lessfile

from $source.lessfile
    .pipe __()
    .pipe $less()
    .pipe $rename
            suffix: '.less'
    .pipe $if(opts.min, $minify_css())
    .pipe $if(opts.min, $rename({ suffix: '.min' }))
    .pipe $if(opts.min, $size())
    .pipe to($dist.css)

npm "browserify" npm "gulp-uglify" npm "gulp-size"

alternate import names

npm "vinyl-buffer", "buffer" npm "vinyl-source-stream", "source_stream"

$.task 'browserify', -> if exists($source.appfile, false) $browserify() .add($source.appfile) .bundle() .pipe $source_stream($source.bundlefile) .pipe $buffer() .pipe $if(opts.min, $uglify()) .pipe $if(opts.min, $rename({ suffix: '.min' })) .pipe $if(opts.min, $size()) .pipe to($dist.js) else log "can't browserify"

npm "gulp-clean"

$.task 'clean', ->

from($dist.files) .pipe print() .pipe $clean({force: true})

$.task 'mkdist', -> # helper $dist.mkdirs

npm "gulp-imagemin"

$.task 'copy', ->

every directory added to source also adds a _files glob pattern

from($source.data_files) .pipe to $dist.data

from($source.images_files) .pipe $if(opts.min, $imagemin()) .pipe to($dist.images)

from($source.fonts_files) .pipe to $dist.fonts

$task is current task

this is for granular livereloading

if $task != "default" log("committing copy") $server.io.emit("change", {type: "copy"})

$.task 'watch', -> watchers = [ ($.watch $source.app_files, ['lint', 'browserify', 'copy']) , ($.watch $source.css_files, ['css', 'copy']) , ($.watch $source.fonts_files, ['copy']) , ($.watch $source.data_files, ['copy']) , ($.watch $source.images_files, ['copy']) , ($.watch $source.templates_files, ["default"]) , ($.watch $source.contents_files, ["default"]) ]

for w in watchers
    w.on('change', (e) ->
        log("file #{e.path} was #{e.type}")
        $server.io.emit("change", e))

npm "gulp-jshint"

$.task 'lint', -> exists $source.jshintfile from $source.app_files .pipe $jshint() .pipe $jshint.reporter('default')

npm "marked"

markdown_parser = (text) -> $marked.setOptions({ renderer: new $marked.Renderer() gfm: true tables: true breaks: false pedantic: true sanitize: true smartLists: true smartpants: true }) $marked(text)

$.task 'compile', -> # contents makes a data structure $contents # out of disk files # this makes it easier to analyse, group and query return contents(markdown: markdown_parser)

npm "nunjucks"

this mutex lock makes sure that the install commands

are run before gulp

$mutex.lock -> env = new $nunjucks.Environment(new $nunjucks.FileSystemLoader($source.templates)); global["$nunjucks"] = env; $mutex.unlock()

$.task "single-page", ['compile'], -> # toStr will Stringify an object writeFile($dist.data + "/content.json", toStr($contents))

lodash has a neat query language inbuilt

$.task 'tags', ['compile'], -> tags = [] .map($contents, (i) -> if i.tags then tags.push(i.tags) else null) .map(.unique(.flatten(tags)), (t) -> tagged_contents = _.where($contents, {tags: [t]}); sorted_posts = _.sortBy(tagged_contents, "title") $nunjucks.render("tags.nunjucks", {tag: t, posts: sorted_posts, opts: opts}, (err, html) -> if err error(err) process.exit(1) else writeFile($dist.html + "/tags/" + t + ".html", html)))

writeFile is a simple helper

$.task 'posts', ['compile'], -> _.map($contents, (i) -> $nunjucks.render("post.nunjucks", {post: i, opts: opts}, (err, html) -> if err error(err) process.exit(1) else writeFile($dist.html + "/" + i.filename + ".html", html)))

$.task 'index', ['compile', 'single-page', 'posts', 'tags'], -> sorted_posts = _.sortBy($contents, "title") sorted_posts = _.filter(sorted_posts, (p) -> not _.has(p,"category")) $nunjucks.render("index.nunjucks", {posts: sorted_posts, opts: opts}, (err, html) -> if err error(err) process.exit(1) else writeFile($dist.html + "/index.html", html))

started by convention

$.task 'default', ['mkdist', 'clean'], -> $.start 'copy', 'css', 'browserify', "index", (err) -> if err error(err) else log("committing default") $server.io.emit("change", {type:"commit"})

npm 'gulp-local-screenshots', "screenshots"

$.task 'screens', -> from $dist.html_files .pipe($screenshots({ port: "8000", path: $dist.html, width: ['1600', '1000', '480', '320'], folder: ($dist.html + "/screenshots") })) .pipe to($dist.html)

start watching


$.task 'serve', -> # server is a static and websocket server port = $args.port || "8080" server dir: $dist.folder, port: port # refresh $server.io.emit("change", {type: "start"}) $server.app.post("/gulp", (req,res) -> # parser for foo: bar baz # rargs to parse a:b,c rargs(req.body.gulp) $.start $rargs[0], (err) -> if err error(err) res.send("ok"))

$.task 'dump', -> dump($server.args)

start server


forget about bower.json

bower directory: $dist.vendor, cwd: $dist.folder, packages: [ "jquery", "sammy" ]


dump($dist) dump($source)

starts gulp

== How is squareboy different ?

squareboy has no forced folder conventions. + squareboy is like rake and make. +

squareboy does not need a plugin system. + use gulp, npm directly. +

squareboy can do what jekyll does. + squareboy does not need a query language like docpad. + use lodash. +

== License


== Status


== Credits

  • https://github.com/greypants/gulp-starter
  • lineman