├── tests ├── common │ └── test0 │ │ ├── src │ │ ├── a.ls │ │ ├── x.js6 │ │ ├── y.js6 │ │ ├── index.ls │ │ ├── packs │ │ │ └── b.ls │ │ └── backends │ │ │ └── c.ls │ │ ├── run.sh │ │ ├── test0.js │ │ ├── test0.js6 │ │ ├── output │ │ └── reference ├── test.sh ├── show.sh └── align.sh ├── .npmignore ├── src ├── index.ls ├── suites │ └── make4web.js6 ├── backends │ ├── make │ │ └── template.mk │ └── make.ls ├── packs │ ├── serve.ls │ ├── livereload.ls │ └── web.ls ├── file.ls └── tree.ls ├── Dockerfile ├── .verb.md ├── README.tpl.md ├── package.json ├── configure.ls ├── .gitignore ├── makefile ├── description.md └── README.md /tests/common/test0/src/a.ls: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/common/test0/src/x.js6: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/common/test0/src/y.js6: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | node_modules/* 2 | -------------------------------------------------------------------------------- /tests/common/test0/src/index.ls: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/common/test0/src/packs/b.ls: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/common/test0/src/backends/c.ls: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/index.ls: -------------------------------------------------------------------------------- 1 | 2 | 3 | module.exports = require('./lib/suites/make4web') 4 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM centos:centos6 2 | # Enable EPEL for Node.js 3 | RUN rpm -Uvh http://download.fedoraproject.org/pub/epel/6/i386/epel-release-6-8.noarch.rpm 4 | # Install Node.js and npm 5 | RUN yum install -y npm 6 | COPY . /src 7 | RUN cd /src; rm -rf node_modules; npm install; npm test; 8 | -------------------------------------------------------------------------------- /.verb.md: -------------------------------------------------------------------------------- 1 | # {%= name %} {%= badge("fury") %} 2 | 3 | > {%= description %} 4 | 5 | ## Install 6 | {%= include("install") %} 7 | 8 | ## API 9 | {%= apidocs("index.js") %} 10 | 11 | ## Author 12 | 13 | **Vittorio Zaccaria** 14 | 15 | + [github/vzaccaria](https://github.com/vzaccaria) 16 | 17 | ## License 18 | {%= copyright() %} 19 | {%= license() %} 20 | 21 | *** 22 | 23 | {%= include("footer") %} 24 | -------------------------------------------------------------------------------- /tests/common/test0/run.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | set -e 3 | 4 | # Source directory 5 | # 6 | srcdir=$(dirname "$0") 7 | srcdir=$(cd "$srcdir"; pwd) 8 | 9 | bindir=$srcdir/../../.. 10 | npm=$bindir/node_modules/.bin 11 | 12 | cd "$srcdir" 13 | rm -f "$srcdir/makefile" 14 | rm -f "$srcdir/output" 15 | node "$srcdir/test0.js" 16 | mv "$srcdir/makefile" "$srcdir/output" 17 | "$npm/diff-files" -m "Test compile, map and reduce" "$srcdir/output" "$srcdir/reference" 18 | -------------------------------------------------------------------------------- /README.tpl.md: -------------------------------------------------------------------------------- 1 | # diy [![NPM version](https://badge.fury.io/js/diy.svg)](http://badge.fury.io/js/diy) 2 | 3 | > do it yourself - build DSL 4 | 5 | {{description}} 6 | 7 | 8 | ## Author 9 | 10 | **Vittorio Zaccaria** 11 | 12 | + [github/vzaccaria](https://github.com/vzaccaria) 13 | 14 | ## License 15 | Copyright (c) 2015 Vittorio Zaccaria 16 | Released under the license 17 | 18 | *** 19 | 20 | _This file was generated by [verb-cli](https://github.com/assemble/verb-cli) on March 10, 2015._ 21 | -------------------------------------------------------------------------------- /tests/test.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | set -e 3 | 4 | # Source directory 5 | # 6 | srcdir=`dirname $0` 7 | srcdir=`cd $srcdir; pwd` 8 | dstdir=`pwd` 9 | 10 | bindir=$srcdir/.. 11 | npm=$srcdir/../node_modules/.bin 12 | 13 | platform=`node -e 'console.log(require("os").platform());'` 14 | 15 | for f in $srcdir/$platform/* 16 | do 17 | # is it a directory? 18 | if [ -d "$f" ]; then 19 | $f/run.sh 20 | fi 21 | done 22 | 23 | for f in $srcdir/common/* 24 | do 25 | # is it a directory? 26 | if [ -d "$f" ]; then 27 | $f/run.sh 28 | fi 29 | done 30 | -------------------------------------------------------------------------------- /src/suites/make4web.js6: -------------------------------------------------------------------------------- 1 | var debug = require('debug')('diy:make4web') 2 | 3 | var { 4 | parse 5 | } = require('../tree') 6 | var { 7 | transcript 8 | } = require('../backends/make') 9 | var webPack = require('../packs/web') 10 | 11 | var _module = () => { 12 | 13 | var generateProject = (body, options) => { 14 | transcript(parse(_ => { 15 | _.addPack(webPack); 16 | body.apply(_,[ _ ]); 17 | }), options); 18 | } 19 | 20 | return { 21 | generateProject: generateProject 22 | } 23 | } 24 | 25 | module.exports = _module() -------------------------------------------------------------------------------- /tests/show.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # Source directory 4 | # 5 | srcdir=`dirname $0` 6 | srcdir=`cd $srcdir; pwd` 7 | dstdir=`pwd` 8 | 9 | bindir=$srcdir/.. 10 | npm=$srcdir/../node_modules/.bin 11 | 12 | platform=`node -e 'console.log(require("os").platform());'` 13 | 14 | for f in $srcdir/$platform/* 15 | do 16 | # is it a directory? 17 | if [ -d "$f" ]; then 18 | diff $f/output $f/reference 19 | fi 20 | done 21 | 22 | if [ -d "$srcdir/common" ]; then 23 | for f in $srcdir/common/* 24 | do 25 | # is it a directory? 26 | if [ -d "$f" ]; then 27 | diff $f/output $f/reference 28 | fi 29 | done 30 | fi 31 | -------------------------------------------------------------------------------- /tests/align.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | set -e 3 | 4 | # Source directory 5 | # 6 | srcdir=`dirname $0` 7 | srcdir=`cd $srcdir; pwd` 8 | dstdir=`pwd` 9 | 10 | bindir=$srcdir/.. 11 | npm=$srcdir/../node_modules/.bin 12 | 13 | platform=`node -e 'console.log(require("os").platform());'` 14 | 15 | for f in $srcdir/$platform/* 16 | do 17 | # is it a directory? 18 | if [ -d "$f" ]; then 19 | cp $f/output $f/reference 20 | fi 21 | done 22 | 23 | 24 | if [ -d "$srcdir/common" ]; then 25 | for f in $srcdir/common/* 26 | do 27 | # is it a directory? 28 | if [ -d "$f" ]; then 29 | cp $f/output $f/reference 30 | fi 31 | done 32 | fi 33 | -------------------------------------------------------------------------------- /src/backends/make/template.mk: -------------------------------------------------------------------------------- 1 | # Makefile autogenerated by Dyi on {{info.today}} 2 | # 3 | # Main target: {{options.defaultGoal}} 4 | # Sources: {% for s in data.sources %} {{s}} {% endfor %} 5 | 6 | .DEFAULT_GOAL := {{options.defaultGoal}} 7 | 8 | {% for p in data.phonyTargets %} 9 | .PHONY: {{p.name}} 10 | {{p.name}}:{% for x in p.dependencies %} {{x}}{% endfor %} 11 | 12 | {% endfor %} 13 | 14 | {% for p in data.phonySequentialTargets %} 15 | .PHONY: {{p.name}} 16 | {{p.name}}: {% for x in p.dependencies %} 17 | make {{x}} {% endfor %} 18 | {% for a in p.actions %} {{a}} 19 | {% endfor %} 20 | {% endfor %} 21 | 22 | {% for t in data.targets %} 23 | {{t.name}}: {% for x in t.dependencies %}{{x}} {% endfor %} 24 | {{t.command}} 25 | {% endfor %} 26 | -------------------------------------------------------------------------------- /src/backends/make.ls: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | debug = require('debug')('backends/make') 4 | moment = require('moment') 5 | { cat } = require('shelljs') 6 | 7 | fs = require('fs') 8 | bb = require('bluebird') 9 | 10 | fs = bb.promisifyAll(fs) 11 | 12 | Liquid = require("liquid-node") 13 | engine = new Liquid.Engine 14 | 15 | _module = -> 16 | 17 | fillWithData = (data) -> 18 | template = cat("#{__dirname}/../../src/backends/make/template.mk") 19 | return engine.parseAndRender(template, data) 20 | 21 | iface = { 22 | 23 | fillWithData: fillWithData 24 | 25 | transcript: (f, options) -> 26 | meta = f.getMeta(options) 27 | meta.info = { today: moment().format('MMMM D, YYYY') } 28 | fillWithData(meta).then -> 29 | fs.writeFileAsync("./makefile", it, "utf8") 30 | 31 | } 32 | 33 | return iface 34 | 35 | module.exports = _module() 36 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "diy-build", 3 | "version": "1.0.9", 4 | "description": "do it yourself - build DSL", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "./tests/test.sh" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/vzaccaria/dyi.git" 12 | }, 13 | "author": "Vittorio Zaccaria", 14 | "license": "BSD", 15 | "bugs": { 16 | "url": "https://github.com/vzaccaria/dyi/issues" 17 | }, 18 | "homepage": "https://github.com/vzaccaria/dyi", 19 | "dependencies": { 20 | "babel": "^4.6.6", 21 | "bluebird": "^2.9.12", 22 | "connect-livereload": "^0.5.3", 23 | "debug": "^2.1.1", 24 | "diff-files": "^0.1.0", 25 | "docopt": "^0.4.1", 26 | "express": "^4.12.0", 27 | "glob": "^4.4.0", 28 | "liquid-node": "^2.4.0", 29 | "lodash": "^3.3.0", 30 | "minimatch": "^2.0.1", 31 | "moment": "^2.9.0", 32 | "pm2": "^0.12.6", 33 | "rewire": "^2.3.1", 34 | "shelljs": "^0.3.0", 35 | "stupid-replace": "0.0.3", 36 | "tiny-lr": "^0.1.5", 37 | "tree-model": "^0.4.3", 38 | "uid": "0.0.2", 39 | "xcode": "^0.6.7", 40 | "xyz": "^0.5.0" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/packs/serve.ls: -------------------------------------------------------------------------------- 1 | 2 | debug = require('debug')('diy:lib/packs/serve') 3 | minimatch = require('minimatch') 4 | {docopt} = require('docopt') 5 | _ = require('lodash') 6 | path = require('path') 7 | 8 | doc = """ 9 | Usage: 10 | serve ROOT [ -p PORT ] 11 | serve -h | --help 12 | 13 | Options: 14 | -p, --port PORT PORT 15 | 16 | Arguments: 17 | ROOT Root to serve the files from; files are served with livereload code baked in 18 | 19 | """ 20 | 21 | get-option = (a, b, def, o) -> 22 | if not o[a] and not o[b] 23 | return def 24 | else 25 | return o[b] 26 | 27 | 28 | get-options = -> 29 | o = docopt(doc) 30 | port = get-option('-p', '--port', '4000', o) 31 | root = o['ROOT'] 32 | return { root, port } 33 | 34 | startExpress = (EXPRESS_ROOT, EXPRESS_PORT) -> 35 | express = require('express'); 36 | app = express(); 37 | app.use(require('connect-livereload')()) 38 | app.use(express.static(EXPRESS_ROOT, {maxAge: 0})); 39 | app.listen(EXPRESS_PORT); 40 | debug "Serving #{EXPRESS_ROOT} at port #{EXPRESS_PORT}" 41 | debug "Added connect-livereload: " 42 | 43 | 44 | main = -> 45 | { root, port } = op = get-options! 46 | debug op 47 | startExpress root, port 48 | 49 | main! 50 | -------------------------------------------------------------------------------- /configure.ls: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env lsc 2 | 3 | { parse, add-plugin } = require('newmake') 4 | 5 | parse -> 6 | 7 | @add-plugin "es6", (g) -> 8 | @compile-files( (-> "./node_modules/.bin/babel #{it.orig-complete} -o #{it.build-target}" ) , ".js", g) 9 | 10 | @add-plugin "lsc", (g) -> 11 | @compile-files( (-> "lsc -b -p -c #{it.orig-complete} > #{it.build-target}" ) , ".js", g) 12 | 13 | @collect "build", -> [ 14 | @toDir "./lib", { strip: "src" }, -> [ 15 | @es6 "src/**/*.js6" 16 | @lsc "src/**/*.ls" 17 | ] 18 | 19 | @toDir ".", -> [ 20 | @es6 "tests/common/test0/*.js6" 21 | ] 22 | ] 23 | 24 | @collect "all", -> [ 25 | @command-seq -> [ 26 | @make \build 27 | @cmd "cp ./lib/index.js ." 28 | @cmd "chmod +x ./lib/packs/serve.js" 29 | @cmd "chmod +x ./lib/packs/livereload.js" 30 | @cmd "./tests/test.sh" 31 | @cmd "cat README.tpl.md | ./node_modules/.bin/stupid-replace '{{description}}' -f description.md > README.md" 32 | ] 33 | ] 34 | 35 | @collect "clean", -> [ 36 | @command-seq -> 37 | @remove-all-targets() 38 | ] 39 | 40 | for l in ["major", "minor", "patch"] 41 | @collect "release-#l", -> [ 42 | @cmd "./node_modules/.bin/xyz --increment #l" 43 | ] 44 | -------------------------------------------------------------------------------- /tests/common/test0/test0.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var rewire = require("rewire"); 4 | 5 | var x = 0; 6 | var y = 0; 7 | 8 | var injectTestDeps = function (mod) { 9 | 10 | mod.__set__("uid", function () { 11 | x = x + 1; 12 | return x; 13 | }); 14 | 15 | mod.__set__("moment", function () { 16 | return { 17 | format: function () { 18 | return ""; 19 | } 20 | }; 21 | }); 22 | }; 23 | 24 | var file = rewire("../../../lib/file"); 25 | injectTestDeps(file); 26 | 27 | var tree = rewire("../../../lib/tree"); 28 | injectTestDeps(tree); 29 | 30 | var webPack = rewire("../../../lib/packs/web"); 31 | injectTestDeps(webPack); 32 | 33 | var make = rewire("../../../lib/backends/make"); 34 | injectTestDeps(make); 35 | 36 | var transcript = make.transcript; 37 | var parse = tree.parse; 38 | 39 | var f = parse(function (_) { 40 | _.addPack(webPack); 41 | _.collect("all", function (_) { 42 | _.mirrorTo("./lib", { 43 | strip: "src/" 44 | }, function (_) { 45 | _.collect("build", function (_) { 46 | _.minify(function (_) { 47 | _.livescript("src/*.ls", "src/packs/*.ls", "src/backends/*.ls"); 48 | _.livescript("src/*.ls"); 49 | _.concat(function (_) { 50 | _.copy("src/*.js6"); 51 | }); 52 | }); 53 | }); 54 | }); 55 | 56 | _.collect("update", function (_) { 57 | _.cmd("echo 'updated'"); 58 | }); 59 | 60 | _.toFile("./index.js", function (_) { 61 | _.minify(function (_) { 62 | _.livescript("src/index.ls"); 63 | }); 64 | _.copy("src/index.ls"); 65 | }); 66 | }); 67 | }); 68 | 69 | transcript(f); 70 | -------------------------------------------------------------------------------- /tests/common/test0/test0.js6: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var rewire = require("rewire"); 4 | 5 | 6 | 7 | var x = 0 8 | var y = 0 9 | 10 | var injectTestDeps = (mod) => { 11 | 12 | mod.__set__('uid', () => { 13 | x = x + 1; 14 | return x 15 | }) 16 | 17 | mod.__set__('moment', () => { 18 | return { 19 | format: () => { 20 | return "" 21 | } 22 | } 23 | }) 24 | } 25 | 26 | 27 | var file = rewire(`../../../lib/file`) 28 | injectTestDeps(file) 29 | 30 | var tree = rewire(`../../../lib/tree`) 31 | injectTestDeps(tree) 32 | 33 | var webPack = rewire(`../../../lib/packs/web`) 34 | injectTestDeps(webPack) 35 | 36 | var make = rewire(`../../../lib/backends/make`) 37 | injectTestDeps(make) 38 | 39 | 40 | 41 | var { 42 | transcript 43 | } = make 44 | var { 45 | parse 46 | } = tree 47 | 48 | var f = parse(_ => { 49 | _.addPack(webPack) 50 | _.collect("all", _ => { 51 | _.mirrorTo("./lib", { 52 | strip: 'src/' 53 | }, _ => { 54 | _.collect("build", _ => { 55 | _.minify(_ => { 56 | _.livescript("src/*.ls", "src/packs/*.ls", "src/backends/*.ls") 57 | _.livescript("src/*.ls") 58 | _.concat( 59 | _ => { 60 | _.copy("src/*.js6") 61 | }) 62 | }) 63 | 64 | }) 65 | }) 66 | 67 | _.collect("update", _ => { 68 | _.cmd("echo 'updated'") 69 | }) 70 | 71 | _.toFile("./index.js", _ => { 72 | _.minify(_ => { 73 | _.livescript("src/index.ls") 74 | }) 75 | _.copy("src/index.ls") 76 | }) 77 | 78 | 79 | }) 80 | }) 81 | 82 | transcript(f); 83 | -------------------------------------------------------------------------------- /src/packs/livereload.ls: -------------------------------------------------------------------------------- 1 | 2 | debug = require('debug')('diy:lib/packs/livereload') 3 | minimatch = require('minimatch') 4 | {docopt} = require('docopt') 5 | _ = require('lodash') 6 | path = require('path') 7 | 8 | doc = """ 9 | Usage: 10 | livereload GLOB [ -s STRIP ] 11 | livereload -h | --help 12 | 13 | Options: 14 | -s, --strip STRIP strip STRIP prefix from files sent to the livereload server 15 | 16 | Arguments 17 | GLOB specifies files to be watched (in quotes, e.g. '**/*') 18 | 19 | """ 20 | 21 | get-option = (a, b, def, o) -> 22 | if not o[a] and not o[b] 23 | return def 24 | else 25 | return o[b] 26 | 27 | 28 | get-options = -> 29 | 30 | o = docopt(doc) 31 | strip = get-option('-s' , '--strip' , "" , o) 32 | files = o['GLOB'] 33 | 34 | return { strip, files } 35 | 36 | LIVERELOAD_PORT = 35729; 37 | 38 | var lr 39 | start-livereload = -> 40 | lr := require('tiny-lr')() 41 | debug "Starting livereload at #LIVERELOAD_PORT" 42 | lr.listen(LIVERELOAD_PORT) 43 | 44 | notify-change = (strip, path) -> 45 | fileName = require('path').relative(strip, path) 46 | found = false 47 | 48 | delay = setTimeout(_, 100) 49 | debug("Notifying Livereload for a change to #{fileName}") 50 | 51 | delay -> 52 | lr.changed body: { files: [fileName] } 53 | 54 | 55 | watch-dest-files = (notify-targets, cb) -> 56 | Gaze = require('gaze').Gaze; 57 | 58 | gaze = new Gaze(notify-targets); 59 | 60 | gaze.on 'ready', -> 61 | debug "Watching destinations" 62 | 63 | gaze.on 'all', cb 64 | 65 | main = (argv) -> 66 | { files, strip } = op = get-options! 67 | debug op 68 | debug "Watching for livereload #{files}" 69 | start-livereload! 70 | watch-dest-files files, (event, filepath) -> 71 | notify-change strip, filepath 72 | 73 | main(process.argv) 74 | -------------------------------------------------------------------------------- /src/packs/web.ls: -------------------------------------------------------------------------------- 1 | debug = require('debug')('newmake/packs') 2 | glob = require('glob') 3 | uid = require('uid') 4 | path = require('path') 5 | 6 | process-stderr-with = (command, processor) -> 7 | "/bin/bash -c \"(#command) 2> >(#processor 1>&2)\"" 8 | 9 | 10 | _module = -> 11 | 12 | iface = { 13 | livescript: (dir, deps) -> 14 | command = (_) -> "lsc -c #{_.source}" 15 | product = (_) -> "#{_.source.replace(/\..*/, '.js')}" 16 | args = [ command, product ] ++ &[0 to ] 17 | @compileFiles.apply(@, args) 18 | 19 | livescriptXc: (dir, deps) -> 20 | command = (_) -> process-stderr-with "lsc -c #{_.source}", "xcparse" 21 | product = (_) -> "#{_.source.replace(/\..*/, '.js')}" 22 | args = [ command, product ] ++ &[0 to ] 23 | @compileFiles.apply(@, args) 24 | 25 | sixToFive: (dir, deps) -> 26 | command = (_) -> "6to5 #{_.source} -o #{_.product}" 27 | product = (_) -> "#{_.source.replace(/\..*/, '.js')}" 28 | args = [ command, product ] ++ &[0 to ] 29 | @compileFiles.apply(@, args) 30 | 31 | js: (dir, deps) -> 32 | command = (_) -> "cp #{_.source} #{_.product}" 33 | product = (_) -> "copy-#{uid(4)}.js" 34 | args = [ command, product ] ++ &[0 to ] 35 | @compileFiles.apply(@, args) 36 | 37 | concat: (body) -> 38 | command = (_) -> "cat #{_.sources * ' '} > #{_.product}" 39 | product = (_) -> "concat-#{uid(4)}.js" 40 | @reduceFiles(command, product, body) 41 | 42 | minify: (body) -> 43 | command = (_) -> "uglifyjs #{_.source} > #{_.product}" 44 | product = (_) -> "minified-#{uid(4)}.js" 45 | @processFiles(command, product, body) 46 | 47 | cmdStdOut: (cmdf, body) -> 48 | command = (_) -> "#{cmdf(_.source)} > #{_.product}" 49 | product = (_) -> "cmd-#{uid(4)}" 50 | args = [ command, product ] ++ &[1 to ] 51 | @processFiles(command, product, body) 52 | 53 | toFile: (name, body) -> 54 | command = (_) -> "cat #{_.sources * ' '} > #{_.product}" 55 | product = (_) -> name 56 | @reduceFiles(command, product, body) 57 | 58 | startWatch: (glob, strip) -> 59 | glob ?= '.' 60 | cmd = "#{__dirname}/../../node_modules/.bin/pm2 start #{__dirname}/livereload.js -- '#glob'" 61 | if strip? 62 | cmd := cmd + " -s #strip " 63 | @cmd(cmd) 64 | 65 | stopWatch: (strip) -> 66 | @cmd("#{__dirname}/../../node_modules/.bin/pm2 delete #{__dirname}/livereload.js") 67 | 68 | startServe: (root, port) -> 69 | root ?= '.' 70 | port ?= 4000 71 | @cmd("#{__dirname}/../../node_modules/.bin/pm2 start #{__dirname}/serve.js -- #root -p #port") 72 | 73 | stopServe: (root) -> 74 | @cmd("#{__dirname}/../../node_modules/.bin/pm2 delete #{__dirname}/serve.js") 75 | 76 | } 77 | 78 | return iface 79 | 80 | module.exports = _module() 81 | -------------------------------------------------------------------------------- /tests/common/test0/output: -------------------------------------------------------------------------------- 1 | # Makefile autogenerated by Dyi on 2 | # 3 | # Main target: all 4 | # Sources: src/a.ls src/index.ls src/x.js6 src/y.js6 5 | 6 | .DEFAULT_GOAL := all 7 | 8 | 9 | .PHONY: c-1 10 | c-1: src/a.js src/index.js 11 | 12 | 13 | .PHONY: c-2 14 | c-2: src/a.js src/index.js 15 | 16 | 17 | .PHONY: c-3 18 | c-3: src/x.js6 src/y.js6 19 | 20 | 21 | .PHONY: r-4 22 | r-4: concat-12.js 23 | 24 | 25 | .PHONY: p-5 26 | p-5: minified-13.js minified-14.js minified-15.js minified-16.js minified-17.js 27 | 28 | 29 | .PHONY: build 30 | build: p-5 31 | 32 | 33 | .PHONY: m-6 34 | m-6: lib/minified-13.js lib/minified-14.js lib/minified-15.js lib/minified-16.js lib/minified-17.js 35 | 36 | 37 | .PHONY: update 38 | update: k-7 39 | 40 | 41 | .PHONY: c-8 42 | c-8: src/index.js 43 | 44 | 45 | .PHONY: p-9 46 | p-9: minified-18.js 47 | 48 | 49 | .PHONY: c-10 50 | c-10: src/index.ls 51 | 52 | 53 | .PHONY: r-11 54 | r-11: ./index.js 55 | 56 | 57 | .PHONY: all 58 | all: m-6 update r-11 59 | 60 | 61 | .PHONY: prepare 62 | prepare: src . lib 63 | 64 | 65 | 66 | 67 | .PHONY: k-7 68 | k-7: 69 | echo 'updated' 70 | 71 | 72 | .PHONY: clean 73 | clean: 74 | rm -f src/a.js 75 | rm -f src/index.js 76 | @echo 'Not cleaning up src/x.js6 because is a source' 77 | @echo 'Not cleaning up src/y.js6 because is a source' 78 | rm -f concat-12.js 79 | rm -f minified-13.js 80 | rm -f minified-14.js 81 | rm -f minified-15.js 82 | rm -f minified-16.js 83 | rm -f minified-17.js 84 | rm -f lib/minified-13.js 85 | rm -f lib/minified-14.js 86 | rm -f lib/minified-15.js 87 | rm -f lib/minified-16.js 88 | rm -f lib/minified-17.js 89 | rm -f minified-18.js 90 | @echo 'Not cleaning up src/index.ls because is a source' 91 | rm -f index.js 92 | 93 | 94 | 95 | 96 | src/a.js: src/a.ls src/packs/b.ls src/backends/c.ls 97 | lsc -c src/a.ls 98 | 99 | src/index.js: src/index.ls src/packs/b.ls src/backends/c.ls 100 | lsc -c src/index.ls 101 | 102 | concat-12.js: src/x.js6 src/y.js6 103 | cat src/x.js6 src/y.js6 > concat-12.js 104 | 105 | minified-13.js: src/a.js 106 | uglifyjs src/a.js > minified-13.js 107 | 108 | minified-14.js: src/index.js 109 | uglifyjs src/index.js > minified-14.js 110 | 111 | minified-15.js: src/a.js 112 | uglifyjs src/a.js > minified-15.js 113 | 114 | minified-16.js: src/index.js 115 | uglifyjs src/index.js > minified-16.js 116 | 117 | minified-17.js: concat-12.js 118 | uglifyjs concat-12.js > minified-17.js 119 | 120 | lib/minified-13.js: minified-13.js 121 | cp minified-13.js ./lib/minified-13.js 122 | 123 | lib/minified-14.js: minified-14.js 124 | cp minified-14.js ./lib/minified-14.js 125 | 126 | lib/minified-15.js: minified-15.js 127 | cp minified-15.js ./lib/minified-15.js 128 | 129 | lib/minified-16.js: minified-16.js 130 | cp minified-16.js ./lib/minified-16.js 131 | 132 | lib/minified-17.js: minified-17.js 133 | cp minified-17.js ./lib/minified-17.js 134 | 135 | minified-18.js: src/index.js 136 | uglifyjs src/index.js > minified-18.js 137 | 138 | index.js: minified-18.js src/index.ls 139 | cat minified-18.js src/index.ls > ./index.js 140 | 141 | src: 142 | mkdir -p src 143 | 144 | .: 145 | mkdir -p . 146 | 147 | lib: 148 | mkdir -p lib 149 | 150 | -------------------------------------------------------------------------------- /tests/common/test0/reference: -------------------------------------------------------------------------------- 1 | # Makefile autogenerated by Dyi on 2 | # 3 | # Main target: all 4 | # Sources: src/a.ls src/index.ls src/x.js6 src/y.js6 5 | 6 | .DEFAULT_GOAL := all 7 | 8 | 9 | .PHONY: c-1 10 | c-1: src/a.js src/index.js 11 | 12 | 13 | .PHONY: c-2 14 | c-2: src/a.js src/index.js 15 | 16 | 17 | .PHONY: c-3 18 | c-3: src/x.js6 src/y.js6 19 | 20 | 21 | .PHONY: r-4 22 | r-4: concat-12.js 23 | 24 | 25 | .PHONY: p-5 26 | p-5: minified-13.js minified-14.js minified-15.js minified-16.js minified-17.js 27 | 28 | 29 | .PHONY: build 30 | build: p-5 31 | 32 | 33 | .PHONY: m-6 34 | m-6: lib/minified-13.js lib/minified-14.js lib/minified-15.js lib/minified-16.js lib/minified-17.js 35 | 36 | 37 | .PHONY: update 38 | update: k-7 39 | 40 | 41 | .PHONY: c-8 42 | c-8: src/index.js 43 | 44 | 45 | .PHONY: p-9 46 | p-9: minified-18.js 47 | 48 | 49 | .PHONY: c-10 50 | c-10: src/index.ls 51 | 52 | 53 | .PHONY: r-11 54 | r-11: ./index.js 55 | 56 | 57 | .PHONY: all 58 | all: m-6 update r-11 59 | 60 | 61 | .PHONY: prepare 62 | prepare: src . lib 63 | 64 | 65 | 66 | 67 | .PHONY: k-7 68 | k-7: 69 | echo 'updated' 70 | 71 | 72 | .PHONY: clean 73 | clean: 74 | rm -f src/a.js 75 | rm -f src/index.js 76 | @echo 'Not cleaning up src/x.js6 because is a source' 77 | @echo 'Not cleaning up src/y.js6 because is a source' 78 | rm -f concat-12.js 79 | rm -f minified-13.js 80 | rm -f minified-14.js 81 | rm -f minified-15.js 82 | rm -f minified-16.js 83 | rm -f minified-17.js 84 | rm -f lib/minified-13.js 85 | rm -f lib/minified-14.js 86 | rm -f lib/minified-15.js 87 | rm -f lib/minified-16.js 88 | rm -f lib/minified-17.js 89 | rm -f minified-18.js 90 | @echo 'Not cleaning up src/index.ls because is a source' 91 | rm -f index.js 92 | 93 | 94 | 95 | 96 | src/a.js: src/a.ls src/packs/b.ls src/backends/c.ls 97 | lsc -c src/a.ls 98 | 99 | src/index.js: src/index.ls src/packs/b.ls src/backends/c.ls 100 | lsc -c src/index.ls 101 | 102 | concat-12.js: src/x.js6 src/y.js6 103 | cat src/x.js6 src/y.js6 > concat-12.js 104 | 105 | minified-13.js: src/a.js 106 | uglifyjs src/a.js > minified-13.js 107 | 108 | minified-14.js: src/index.js 109 | uglifyjs src/index.js > minified-14.js 110 | 111 | minified-15.js: src/a.js 112 | uglifyjs src/a.js > minified-15.js 113 | 114 | minified-16.js: src/index.js 115 | uglifyjs src/index.js > minified-16.js 116 | 117 | minified-17.js: concat-12.js 118 | uglifyjs concat-12.js > minified-17.js 119 | 120 | lib/minified-13.js: minified-13.js 121 | cp minified-13.js ./lib/minified-13.js 122 | 123 | lib/minified-14.js: minified-14.js 124 | cp minified-14.js ./lib/minified-14.js 125 | 126 | lib/minified-15.js: minified-15.js 127 | cp minified-15.js ./lib/minified-15.js 128 | 129 | lib/minified-16.js: minified-16.js 130 | cp minified-16.js ./lib/minified-16.js 131 | 132 | lib/minified-17.js: minified-17.js 133 | cp minified-17.js ./lib/minified-17.js 134 | 135 | minified-18.js: src/index.js 136 | uglifyjs src/index.js > minified-18.js 137 | 138 | index.js: minified-18.js src/index.ls 139 | cat minified-18.js src/index.ls > ./index.js 140 | 141 | src: 142 | mkdir -p src 143 | 144 | .: 145 | mkdir -p . 146 | 147 | lib: 148 | mkdir -p lib 149 | 150 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .build 2 | index.js 3 | lib/test/treeTest.js 4 | lib/tree.js 5 | new.mk 6 | src/backends/make.js 7 | src/file.js 8 | src/packs/web.js 9 | src/tree.js 10 | lib/backends/make.js 11 | lib/file.js 12 | lib/index.js 13 | lib/packs/web.js 14 | lib 15 | tests/common/test0/output 16 | tests/darwin/test0/output 17 | tests/darwin/test1/output 18 | tests/darwin/test1/Product.xcodeproj/project.pbxproj 19 | tests/darwin/test1/Product.xcodeproj/project.xcworkspace/contents.xcworkspacedata 20 | tests/darwin/test1/Product.xcodeproj/project.xcworkspace/xcshareddata/Product.xccheckout 21 | tests/darwin/test1/Product.xcodeproj/project.xcworkspace/xcuserdata/zaccaria.xcuserdatad/UserInterfaceState.xcuserstate 22 | tests/darwin/test1/Product.xcodeproj/xcuserdata/zaccaria.xcuserdatad/xcschemes/Product.xcscheme 23 | tests/darwin/test1/Product.xcodeproj/xcuserdata/zaccaria.xcuserdatad/xcschemes/xcschememanagement.plist 24 | tests/darwin/test1/minified-10.js 25 | tests/darwin/test1/minified-11.js 26 | tests/darwin/test1/minified-12.js 27 | tests/darwin/test1/minified-13.js 28 | tests/darwin/test1/minified-14.js 29 | tests/darwin/test1/src/a.js 30 | tests/darwin/test1/src/backends/c.js 31 | tests/darwin/test1/src/packs/b.js 32 | tests/darwin/test2/minified-10.js 33 | tests/darwin/test2/minified-11.js 34 | tests/darwin/test2/minified-12.js 35 | tests/darwin/test2/minified-13.js 36 | tests/darwin/test2/minified-14.js 37 | tests/darwin/test2/output 38 | tests/darwin/test2/makefile 39 | tests/darwin/test2/Product.xcodeproj 40 | tests/darwin/test2/src/a.js 41 | tests/darwin/test2/src/backends/c.js 42 | tests/darwin/test2/src/packs/b.js 43 | tests/darwin/test3/Product.xcodeproj/project.pbxproj 44 | tests/darwin/test3/Product.xcodeproj/project.xcworkspace/contents.xcworkspacedata 45 | tests/darwin/test3/Product.xcodeproj/project.xcworkspace/xcshareddata/Product.xccheckout 46 | tests/darwin/test3/Product.xcodeproj/project.xcworkspace/xcuserdata/zaccaria.xcuserdatad/UserInterfaceState.xcuserstate 47 | tests/darwin/test3/Product.xcodeproj/xcuserdata/zaccaria.xcuserdatad/xcschemes/Product.xcscheme 48 | tests/darwin/test3/Product.xcodeproj/xcuserdata/zaccaria.xcuserdatad/xcschemes/xcschememanagement.plist 49 | tests/darwin/test3/src/backends/c.js 50 | tests/darwin/test3/src/packs/b.js 51 | tests/darwin/test4/output 52 | tests/darwin/test4/src/backends/c.js 53 | tests/darwin/test4/src/packs/b.js 54 | templates/v.md 55 | npm-debug.log 56 | tests/linux/test0/output 57 | tests/linux/test1/makefile 58 | tests/linux/test1/minified-10.js 59 | tests/linux/test1/minified-11.js 60 | tests/linux/test1/minified-12.js 61 | tests/linux/test1/minified-13.js 62 | tests/linux/test1/minified-14.js 63 | tests/linux/test1/output 64 | tests/linux/test1/Product.xcodeproj 65 | tests/linux/test1/src/a.js 66 | tests/linux/test1/src/backends/c.js 67 | tests/linux/test1/src/packs/b.js 68 | tests/linux/test2/makefile 69 | tests/linux/test2/minified-10.js 70 | tests/linux/test2/minified-11.js 71 | tests/linux/test2/minified-12.js 72 | tests/linux/test2/minified-13.js 73 | tests/linux/test2/minified-14.js 74 | tests/linux/test2/output 75 | tests/linux/test2/Product.xcodeproj 76 | tests/linux/test2/src/a.js 77 | tests/linux/test2/src/backends/c.js 78 | tests/linux/test2/src/packs/b.js 79 | tests/linux/test3/output 80 | tests/linux/test3/Product.xcodeproj 81 | tests/linux/test3/src/backends/c.js 82 | tests/linux/test3/src/packs/b.js 83 | tests/linux/test4/src/backends/c.js 84 | tests/linux/test4/src/packs/b.js 85 | -------------------------------------------------------------------------------- /src/file.ls: -------------------------------------------------------------------------------- 1 | _ = require('lodash') 2 | debug = require('debug')('diy:file') 3 | assert = require('assert') 4 | path = require('path') 5 | moment = require('moment') 6 | 7 | class target 8 | (@name, @depNames) ~> 9 | debug "Creating new target: #{@name} - deps: #{depNames}" 10 | debug @depNames 11 | assert(@name) 12 | assert(_.is-array(@depNames)) 13 | _.map @depNames, -> 14 | assert(_.is-string(it)) 15 | @name = path.normalize(@name) 16 | _.map @depNames, -> 17 | path.normalize(it) 18 | 19 | class phony extends target 20 | (@name, @depNames, @options) ~> 21 | assert(_.is-object(@options)) 22 | super(@name, @depNames) 23 | 24 | class product extends target 25 | (@name, @productOf, @command, @deps) ~> 26 | debug("Created product #{@name}") 27 | assert(@command) 28 | super(@name, @deps) 29 | 30 | 31 | 32 | class targetStore 33 | ~> 34 | @_targets = {} 35 | 36 | ensureTargetIsPhony: (tName) ~> 37 | assert(@_targets[tName].constructor.name == 'phony') 38 | 39 | addTarget: (target) ~> 40 | @_targets[target.name] = target 41 | 42 | addSources: (sources) ~> 43 | @_sources = sources 44 | 45 | getPhonyTargetNames: ~> 46 | _.keys(_.omit @_targets, (.constructor.name != 'phony')) # on values 47 | 48 | getActualTargetNames: ~> 49 | trgts = _.difference(_.keys(_.omit @_targets, (.constructor.name == 'phony')), @_sources) # Exclude sources. 50 | 51 | getTargetDepsAsNames: (tName) ~> 52 | _.uniq(@_targets[tName].depNames) 53 | 54 | getPhonyTargetActions: (tName) ~> 55 | @ensureTargetIsPhony(tName) 56 | if @_targets[tName].options?.actions? 57 | return @_targets[tName].options.actions 58 | else 59 | return [] 60 | 61 | isPhonyTargetSequential: (tName) ~> 62 | @ensureTargetIsPhony(tName) 63 | @_targets[tName].options?.sequential? and @_targets[tName].options.sequential 64 | 65 | getTargetCreationCommand: (tName) ~> 66 | if @_targets[tName].constructor.name == 'phony' 67 | throw "Sorry, #tname is not a construction target" 68 | else 69 | @_targets[tName].command 70 | 71 | getMeta: (options) ~> 72 | options ?= {} 73 | options.default-goal ?= 'all' 74 | 75 | meta = { 76 | options: options 77 | data: 78 | phonyTargets: [] 79 | phonySequentialTargets: [] 80 | targets: [] 81 | sources: @_sources 82 | } 83 | 84 | for k in @getPhonyTargetNames! 85 | if not @isPhonyTargetSequential(k) 86 | meta.data.phonyTargets.push { 87 | name: k 88 | dependencies: @getTargetDepsAsNames(k) 89 | } 90 | else 91 | meta.data.phonySequentialTargets.push { 92 | name: k 93 | dependencies: @getTargetDepsAsNames(k) 94 | actions: @getPhonyTargetActions(k) 95 | } 96 | 97 | for k in @getActualTargetNames! 98 | meta.data.targets.push { 99 | name: k 100 | dependencies: @getTargetDepsAsNames(k) 101 | command: @getTargetCreationCommand(k) 102 | } 103 | 104 | return meta 105 | 106 | 107 | 108 | module.exports = { product, phony, targetStore } 109 | -------------------------------------------------------------------------------- /makefile: -------------------------------------------------------------------------------- 1 | .DEFAULT_GOAL := all 2 | 3 | .build/0-make4web.js: src/suites/make4web.js6 4 | ./node_modules/.bin/babel src/suites/make4web.js6 -o .build/0-make4web.js 5 | 6 | .build/1-make.js: src/backends/make.ls 7 | lsc -b -p -c src/backends/make.ls > .build/1-make.js 8 | 9 | .build/2-file.js: src/file.ls 10 | lsc -b -p -c src/file.ls > .build/2-file.js 11 | 12 | .build/3-index.js: src/index.ls 13 | lsc -b -p -c src/index.ls > .build/3-index.js 14 | 15 | .build/4-livereload.js: src/packs/livereload.ls 16 | lsc -b -p -c src/packs/livereload.ls > .build/4-livereload.js 17 | 18 | .build/5-serve.js: src/packs/serve.ls 19 | lsc -b -p -c src/packs/serve.ls > .build/5-serve.js 20 | 21 | .build/6-web.js: src/packs/web.ls 22 | lsc -b -p -c src/packs/web.ls > .build/6-web.js 23 | 24 | .build/7-tree.js: src/tree.ls 25 | lsc -b -p -c src/tree.ls > .build/7-tree.js 26 | 27 | lib/suites/make4web.js: .build/0-make4web.js 28 | @mkdir -p ./lib//suites 29 | cp .build/0-make4web.js $@ 30 | 31 | lib/backends/make.js: .build/1-make.js 32 | @mkdir -p ./lib//backends 33 | cp .build/1-make.js $@ 34 | 35 | lib/file.js: .build/2-file.js 36 | @mkdir -p ./lib/ 37 | cp .build/2-file.js $@ 38 | 39 | lib/index.js: .build/3-index.js 40 | @mkdir -p ./lib/ 41 | cp .build/3-index.js $@ 42 | 43 | lib/packs/livereload.js: .build/4-livereload.js 44 | @mkdir -p ./lib//packs 45 | cp .build/4-livereload.js $@ 46 | 47 | lib/packs/serve.js: .build/5-serve.js 48 | @mkdir -p ./lib//packs 49 | cp .build/5-serve.js $@ 50 | 51 | lib/packs/web.js: .build/6-web.js 52 | @mkdir -p ./lib//packs 53 | cp .build/6-web.js $@ 54 | 55 | lib/tree.js: .build/7-tree.js 56 | @mkdir -p ./lib/ 57 | cp .build/7-tree.js $@ 58 | 59 | .build/8-test0.js: tests/common/test0/test0.js6 60 | ./node_modules/.bin/babel tests/common/test0/test0.js6 -o .build/8-test0.js 61 | 62 | tests/common/test0/test0.js: .build/8-test0.js 63 | @mkdir -p ./tests/common/test0 64 | cp .build/8-test0.js $@ 65 | 66 | .PHONY : build 67 | build: lib/suites/make4web.js lib/backends/make.js lib/file.js lib/index.js lib/packs/livereload.js lib/packs/serve.js lib/packs/web.js lib/tree.js tests/common/test0/test0.js 68 | 69 | .PHONY : cmd-9 70 | cmd-9: 71 | make build 72 | 73 | .PHONY : cmd-10 74 | cmd-10: 75 | cp ./lib/index.js . 76 | 77 | .PHONY : cmd-11 78 | cmd-11: 79 | chmod +x ./lib/packs/serve.js 80 | 81 | .PHONY : cmd-12 82 | cmd-12: 83 | chmod +x ./lib/packs/livereload.js 84 | 85 | .PHONY : cmd-13 86 | cmd-13: 87 | ./tests/test.sh 88 | 89 | .PHONY : cmd-14 90 | cmd-14: 91 | cat README.tpl.md | ./node_modules/.bin/stupid-replace '{{description}}' -f description.md > README.md 92 | 93 | .PHONY : cmd-seq-15 94 | cmd-seq-15: 95 | make cmd-9 96 | make cmd-10 97 | make cmd-11 98 | make cmd-12 99 | make cmd-13 100 | make cmd-14 101 | 102 | .PHONY : all 103 | all: cmd-seq-15 104 | 105 | .PHONY : clean-16 106 | clean-16: 107 | rm -rf .build/0-make4web.js .build/1-make.js .build/2-file.js .build/3-index.js .build/4-livereload.js .build/5-serve.js .build/6-web.js .build/7-tree.js lib/suites/make4web.js lib/backends/make.js lib/file.js lib/index.js lib/packs/livereload.js lib/packs/serve.js lib/packs/web.js lib/tree.js .build/8-test0.js tests/common/test0/test0.js 108 | 109 | .PHONY : clean-17 110 | clean-17: 111 | rm -rf .build 112 | 113 | .PHONY : clean-18 114 | clean-18: 115 | mkdir -p .build 116 | 117 | .PHONY : cmd-seq-19 118 | cmd-seq-19: 119 | make clean-16 120 | make clean-17 121 | make clean-18 122 | 123 | .PHONY : clean 124 | clean: cmd-seq-19 125 | 126 | .PHONY : cmd-20 127 | cmd-20: 128 | ./node_modules/.bin/xyz --increment major 129 | 130 | .PHONY : release-major 131 | release-major: cmd-20 132 | 133 | .PHONY : cmd-21 134 | cmd-21: 135 | ./node_modules/.bin/xyz --increment minor 136 | 137 | .PHONY : release-minor 138 | release-minor: cmd-21 139 | 140 | .PHONY : cmd-22 141 | cmd-22: 142 | ./node_modules/.bin/xyz --increment patch 143 | 144 | .PHONY : release-patch 145 | release-patch: cmd-22 146 | -------------------------------------------------------------------------------- /description.md: -------------------------------------------------------------------------------- 1 | # install 2 | 3 | ```shell 4 | npm install diy-build 5 | ``` 6 | 7 | # example 8 | ## concatenate and minify javascript files 9 | 10 | Assume you have two files (`src/file1.js` and `src/file2.js`) that you want to 11 | concatenate and minify into a single `_site/client.js` file. Here's how you'd write a DIY (ES6) program for the task at hand: 12 | 13 | ## diy source file 14 | 15 | ```js 16 | /* configure.js */ 17 | 18 | var { 19 | generateProject 20 | } = require("diy-build"); 21 | 22 | generateProject(_ => { 23 | _.collect("all", _ => { 24 | _.toFile( "_site/client.js", _ => { 25 | _.minify( _ => { 26 | _.concat( _ => { 27 | _.glob("src/*.js") 28 | }) 29 | }) 30 | }) 31 | }) 32 | }) 33 | ``` 34 | 35 | ## description 36 | 37 | DIY uses a **top-down** approach in which you decompose the construction of each product/target into simpler steps. 38 | 39 | Everything begins with the `generateProject` library function. This function 40 | generates the makefile by following the steps indicated in the closure passed as a parameter. 41 | 42 | ```js 43 | generateProject(_ => { 44 | _.collect("all", 45 | ... 46 | ``` 47 | 48 | This will declare a new Make target (`all`) associated with the construction of 49 | a set of products specified in the closure passed to `collect` (think of it as a Grunt task). 50 | 51 | You could even have a sequence of `_.collect` to declare multiple targets or even multiple targets nested inside another (to specify nested dependencies): 52 | 53 | 54 | ```js 55 | generateProject(_ => { 56 | _.collectSeq("all", _ => { 57 | _.collect("build", _ => { 58 | /* specify how to build files */ 59 | }) 60 | _.collect("deploy", _ => { 61 | /* specify how to deploy files */ 62 | } 63 | }) 64 | }) 65 | ``` 66 | 67 | Let's go back to our example. The `_.collect` operation specifies the files that must be built: 68 | 69 | ```js 70 | _.collect("all", _ => { 71 | _.toFile( "_site/client.js", _ => { 72 | ... 73 | ``` 74 | 75 | Here, we are specifying that the `all` target corresponds to the construction of the file `_site/client.js`. In this case, 76 | `_.toFile` receives a closure that constructs the minified concatenation of all `src/*.js`: 77 | 78 | ```js 79 | _.toFile( "_site/client.js", _ => { 80 | _.minify( _ => { 81 | _.concat( _ => { 82 | _.glob("src/*.js") 83 | ... 84 | ``` 85 | 86 | **Check out the [docs for additional ways to process files](docs/index.html)** 87 | 88 | ## makefile generation 89 | 90 | 91 | To generate the makefile we use `babel` to get ES5: 92 | 93 | ```bash 94 | babel configure.js | node 95 | ``` 96 | 97 | And here's the [generated makefile](demo/makefile). 98 | 99 | The makefile comes with two default targets (`prepare` and `clean`) and all the targets defined with `collect`: 100 | 101 | ```bash 102 | > make prepare # Creates destination directories 103 | > make clean # Removes all products 104 | > make all # Execute commands associated with `all` 105 | ``` 106 | 107 | Make provides a way to specify the maximum parallelism to be used for building targets: 108 | 109 | ```bash 110 | > make all -j 8 # Build all, execute up to 8 concurrent commands. 111 | ``` 112 | 113 | 114 | 115 | # customization 116 | 117 | What about your favorite CSS/JS preprocessor and other minifiers? 118 | 119 | Here's how you would define a new processing step to compile Javascript with a 120 | bunch of Browserify plugins: 121 | 122 | ```js 123 | _.browserify = (src, ...deps) => { 124 | var command = (_) => `./node_modules/.bin/browserify -t liveify -t node-lessify ${_.source} -o ${_.product}` 125 | var product = (_) => `${_.source.replace(/\..*/, '.bfd.js')}` 126 | _.compileFiles(...([ command, product, src ].concat(deps))) 127 | } 128 | ``` 129 | 130 | `_.compileFiles` is a built-in function that allows you to easily construct new processing steps. Its first 131 | two parameters are two templates: 132 | 133 | 1. A function to build the command line; and 134 | 2. A function to build the product name. 135 | 136 | The remaining parameters are `src` (glob for the source files) and the source dependencies. 137 | 138 | ```js 139 | generateProject(_ => { 140 | 141 | _.browserify = (dir, ...deps) => { 142 | var command = (_) => `./node_modules/.bin/browserify -t liveify -t node-lessify ${_.source} -o ${_.product}` 143 | var product = (_) => `${_.source.replace(/\..*/, '.bfd.js')}` 144 | _.compileFiles(...([ command, product, dir ].concat(deps))) 145 | } 146 | 147 | _.collect("all", _ => { 148 | _.toFile( "_site/client.js", _ => { 149 | _.browserify("src/index.ls", "src/**/*.less", "src/**/*.ls") 150 | }) 151 | }) 152 | } 153 | ``` 154 | 155 | # serving and livereloading 156 | 157 | Serving static files from a directory and livereloading upon a change of a product are supported through `pm2` and `tiny-lr`. We can 158 | create two make targets (`start` and `stop`) that start and stop both services: 159 | 160 | ```js 161 | generateProject(_ => { 162 | 163 | /* ... */ 164 | 165 | _.collect("start", _ => { 166 | _.startWatch("_site/**/*") 167 | _.startServe("_site") 168 | }) 169 | 170 | _.collect("stop", _ => { 171 | _.stopWatch() 172 | _.stopServe() 173 | }) 174 | 175 | /* ... */ 176 | }) 177 | ``` 178 | 179 | `_.startWatch(glob)` is a built-in step that launches a tiny-lr instance to trigger a reload upon a change in files matching the glob. 180 | `_.startServe(root,port)` serves files from the specified root and port. 181 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # diy [![NPM version](https://badge.fury.io/js/diy.svg)](http://badge.fury.io/js/diy) 2 | 3 | > do it yourself - build DSL 4 | 5 | # install 6 | 7 | ```shell 8 | npm install diy-build 9 | ``` 10 | 11 | # example 12 | ## concatenate and minify javascript files 13 | 14 | Assume you have two files (`src/file1.js` and `src/file2.js`) that you want to 15 | concatenate and minify into a single `_site/client.js` file. Here's how you'd write a DIY (ES6) program for the task at hand: 16 | 17 | ## diy source file 18 | 19 | ```js 20 | /* configure.js */ 21 | 22 | var { 23 | generateProject 24 | } = require("diy-build"); 25 | 26 | generateProject(_ => { 27 | _.collect("all", _ => { 28 | _.toFile( "_site/client.js", _ => { 29 | _.minify( _ => { 30 | _.concat( _ => { 31 | _.glob("src/*.js") 32 | }) 33 | }) 34 | }) 35 | }) 36 | }) 37 | ``` 38 | 39 | ## description 40 | 41 | DIY uses a **top-down** approach in which you decompose the construction of each product/target into simpler steps. 42 | 43 | Everything begins with the `generateProject` library function. This function 44 | generates the makefile by following the steps indicated in the closure passed as a parameter. 45 | 46 | ```js 47 | generateProject(_ => { 48 | _.collect("all", 49 | ... 50 | ``` 51 | 52 | This will declare a new Make target (`all`) associated with the construction of 53 | a set of products specified in the closure passed to `collect` (think of it as a Grunt task). 54 | 55 | You could even have a sequence of `_.collect` to declare multiple targets or even multiple targets nested inside another (to specify nested dependencies): 56 | 57 | 58 | ```js 59 | generateProject(_ => { 60 | _.collectSeq("all", _ => { 61 | _.collect("build", _ => { 62 | /* specify how to build files */ 63 | }) 64 | _.collect("deploy", _ => { 65 | /* specify how to deploy files */ 66 | } 67 | }) 68 | }) 69 | ``` 70 | 71 | Let's go back to our example. The `_.collect` operation specifies the files that must be built: 72 | 73 | ```js 74 | _.collect("all", _ => { 75 | _.toFile( "_site/client.js", _ => { 76 | ... 77 | ``` 78 | 79 | Here, we are specifying that the `all` target corresponds to the construction of the file `_site/client.js`. In this case, 80 | `_.toFile` receives a closure that constructs the minified concatenation of all `src/*.js`: 81 | 82 | ```js 83 | _.toFile( "_site/client.js", _ => { 84 | _.minify( _ => { 85 | _.concat( _ => { 86 | _.glob("src/*.js") 87 | ... 88 | ``` 89 | 90 | **Check out the [docs for additional ways to process files](docs/index.html)** 91 | 92 | ## makefile generation 93 | 94 | 95 | To generate the makefile we use `babel` to get ES5: 96 | 97 | ```bash 98 | babel configure.js | node 99 | ``` 100 | 101 | And here's the [generated makefile](demo/makefile). 102 | 103 | The makefile comes with two default targets (`prepare` and `clean`) and all the targets defined with `collect`: 104 | 105 | ```bash 106 | > make prepare # Creates destination directories 107 | > make clean # Removes all products 108 | > make all # Execute commands associated with `all` 109 | ``` 110 | 111 | Make provides a way to specify the maximum parallelism to be used for building targets: 112 | 113 | ```bash 114 | > make all -j 8 # Build all, execute up to 8 concurrent commands. 115 | ``` 116 | 117 | 118 | 119 | # customization 120 | 121 | What about your favorite CSS/JS preprocessor and other minifiers? 122 | 123 | Here's how you would define a new processing step to compile Javascript with a 124 | bunch of Browserify plugins: 125 | 126 | ```js 127 | _.browserify = (src, ...deps) => { 128 | var command = (_) => `./node_modules/.bin/browserify -t liveify -t node-lessify ${_.source} -o ${_.product}` 129 | var product = (_) => `${_.source.replace(/\..*/, '.bfd.js')}` 130 | _.compileFiles(...([ command, product, src ].concat(deps))) 131 | } 132 | ``` 133 | 134 | `_.compileFiles` is a built-in function that allows you to easily construct new processing steps. Its first 135 | two parameters are two templates: 136 | 137 | 1. A function to build the command line; and 138 | 2. A function to build the product name. 139 | 140 | The remaining parameters are `src` (glob for the source files) and the source dependencies. 141 | 142 | ```js 143 | generateProject(_ => { 144 | 145 | _.browserify = (dir, ...deps) => { 146 | var command = (_) => `./node_modules/.bin/browserify -t liveify -t node-lessify ${_.source} -o ${_.product}` 147 | var product = (_) => `${_.source.replace(/\..*/, '.bfd.js')}` 148 | _.compileFiles(...([ command, product, dir ].concat(deps))) 149 | } 150 | 151 | _.collect("all", _ => { 152 | _.toFile( "_site/client.js", _ => { 153 | _.browserify("src/index.ls", "src/**/*.less", "src/**/*.ls") 154 | }) 155 | }) 156 | } 157 | ``` 158 | 159 | # serving and livereloading 160 | 161 | Serving static files from a directory and livereloading upon a change of a product are supported through `pm2` and `tiny-lr`. We can 162 | create two make targets (`start` and `stop`) that start and stop both services: 163 | 164 | ```js 165 | generateProject(_ => { 166 | 167 | /* ... */ 168 | 169 | _.collect("start", _ => { 170 | _.startWatch("_site/**/*") 171 | _.startServe("_site") 172 | }) 173 | 174 | _.collect("stop", _ => { 175 | _.stopWatch() 176 | _.stopServe() 177 | }) 178 | 179 | /* ... */ 180 | }) 181 | ``` 182 | 183 | `_.startWatch(glob)` is a built-in step that launches a tiny-lr instance to trigger a reload upon a change in files matching the glob. 184 | `_.startServe(root,port)` serves files from the specified root and port. 185 | 186 | 187 | 188 | ## Author 189 | 190 | **Vittorio Zaccaria** 191 | 192 | + [github/vzaccaria](https://github.com/vzaccaria) 193 | 194 | ## License 195 | Copyright (c) 2015 Vittorio Zaccaria 196 | Released under the license 197 | 198 | *** 199 | 200 | _This file was generated by [verb-cli](https://github.com/assemble/verb-cli) on March 10, 2015._ 201 | 202 | -------------------------------------------------------------------------------- /src/tree.ls: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | TreeModel = require('tree-model') 4 | debug = require('debug')('diy:tree') 5 | uid = require('uid') 6 | _ = require('lodash') 7 | path = require('path') 8 | glob = require('glob') 9 | 10 | { phony, product, targetStore } = require('./file') 11 | 12 | tree = new TreeModel() 13 | emptyNode = -> tree.parse({}) 14 | 15 | createBuildTarget = (node, source) -> 16 | # debug "/createBuildTarget: building from model #{JSON.stringify(node.model, 0,4)}" 17 | let @=node.model 18 | productName = @product-fun({source: source}) 19 | commandName = @cmd-fun({source: source, product: productName, sources: @sources, deps: @deps}) 20 | f = new product(productName, source, commandName, [source] ++ @deps) 21 | return f 22 | 23 | 24 | processCompileNode = (node) -> 25 | let @=node.model 26 | buildTargets = _.map _.flattenDeep(@sources), (source) -> 27 | createBuildTarget(node, source) 28 | 29 | @products = _.map(buildTargets, (.name)) 30 | buildTargets = buildTargets ++ (new phony(@targetName, @products, {} )) 31 | return buildTargets 32 | 33 | 34 | processPostProcessNode = (node) -> 35 | let @=node.model 36 | @sources = _.flattenDeep(@children.map (.products)) 37 | return processCompileNode(node) 38 | 39 | 40 | processReduceNode = (node) -> 41 | let @=node.model 42 | debug "reduceNode name", @ 43 | 44 | source = "fake-src" 45 | 46 | @deps = _.flattenDeep(@children.map (.products)) 47 | @sources = @deps 48 | 49 | productName = @product-fun({source: source}) 50 | commandName = @cmd-fun({source: source, product: productName, sources: @sources, deps: @deps}) 51 | @products = [ productName ] 52 | 53 | f1 = new product(productName, source, commandName, @deps) 54 | f2 = new phony(@targetName, @products, {} ) 55 | return [ f1, f2 ] 56 | 57 | processCollectNode = (node) -> 58 | let @=node.model 59 | debug "CollectNode name", @ 60 | @deps = _.flattenDeep(@children.map (.targetName)) 61 | @products = _.flattenDeep(@children.map (.products)) 62 | return (new phony(@targetName, @deps, @options)) 63 | 64 | executeCommand = (tName, c) -> 65 | new phony(tName, [] , { +sequential, actions: [ c ] }) 66 | 67 | processCommandNode = (node) -> 68 | let @=node.model 69 | return executeCommand(@targetName, @cmd) 70 | 71 | 72 | 73 | processNode = (node) -> 74 | let @=node.model 75 | buildTargets = 76 | | @type == "compile" => processCompileNode(node) 77 | | @type == "process" or @type == "move" => processPostProcessNode(node) 78 | | @type == "reduce" => processReduceNode(node) 79 | | @type == "root" => [] 80 | | @type == "collect" => processCollectNode(node) 81 | | @type == "command" => processCommandNode(node) 82 | | otherwise => throw "Invalid type `#{@type}`" 83 | return buildTargets 84 | 85 | mapNodes = (r, f) -> 86 | s = [ ] 87 | r.walk {strategy: 'post'}, (node) -> 88 | v = f(node) 89 | s.push(v) 90 | return s 91 | 92 | processNodes = (r) -> 93 | nodeList = mapNodes(r, processNode) 94 | return _.flattenDeep(nodeList) 95 | 96 | forAllProducts = (l, f) -> 97 | _.map(_.filter(l, (.constructor.name == 'product')), f) 98 | 99 | getSources = (r) -> 100 | cNodes = mapNodes(r, _.identity) 101 | cNodes = _.filter(cNodes, (.model.type == 'compile')) 102 | sources = _.map cNodes, (.model.sources) 103 | return _.filter(_.uniq(_.flattenDeep(sources))) 104 | 105 | 106 | ensureDirectoryExists = (p) -> 107 | new product(path.dirname(p.name), [], "mkdir -p #{path.dirname(p.name)}", []) 108 | 109 | 110 | 111 | class treeBuilder 112 | 113 | -> 114 | @curNode = emptyNode! 115 | @curNode.model.targetName = "root" 116 | @curNode.model.type = "root" 117 | 118 | createTree: (body) ~> 119 | newNode = emptyNode! 120 | savedCurNode = @curNode 121 | @curNode = newNode 122 | body.apply(@, [ @ ]) 123 | @curNode = savedCurNode 124 | newNode.model.children = newNode.children.map (.model) 125 | return newNode 126 | 127 | cmd: (comm, deps) ~> 128 | deps ?= [] 129 | @curNode.addChild(tree.parse({type: "command", targetName: "k-#{uid(8)}", cmd: comm, deps: deps })) 130 | 131 | compileFiles: (cmd, product, src) ~> 132 | deps = &[3 to ] 133 | deps ?= [] 134 | debug "deps: #{JSON.stringify(deps)}" 135 | debug "src: #{JSON.stringify(src)}" 136 | src = glob.sync(src) 137 | deps = _.map(deps, (-> glob.sync(it))) |> _.flattenDeep 138 | debug "deps: #{JSON.stringify(deps)}" 139 | @curNode.addChild(tree.parse({cmd-fun: cmd, product-fun: product, sources: src, deps: deps, targetName: "c-#{uid(8)}", type: "compile", children: [] })) 140 | 141 | processFiles: (cmd, product, body) ~> 142 | root = @createTree(body) 143 | _.extend(root.model, {cmd-fun: cmd, product-fun: product, targetName: "p-#{uid(8)}", type: "process", deps: []}) 144 | @curNode.addChild(root) 145 | 146 | reduceFiles: (cmd, product, body) ~> 147 | root = @createTree(body) 148 | _.extend(root.model, {cmd-fun: cmd, product-fun: product, targetName: "r-#{uid(8)}", type: "reduce", deps: []}) 149 | @curNode.addChild(root) 150 | 151 | _collect: (name, body, options) ~> 152 | root = @createTree(body) 153 | _.extend(root.model, { targetName: name, options: options, type: "collect" }) 154 | @curNode.addChild(root) 155 | 156 | collect: (name, body) ~> 157 | @_collect(name, body, {}) 158 | 159 | copy: (dir, deps) ~> 160 | command = (_) -> "echo 'using #{_.source} as product'" 161 | product = (_) -> _.source 162 | args = [ command, product ] ++ &[0 to ] 163 | @compileFiles.apply(@, args) 164 | 165 | glob: ~> 166 | @copy.apply(&[0 to ]) 167 | 168 | mirrorTo: (name, options, body) ~> 169 | if _.is-function(options) 170 | body = options 171 | options = {} 172 | root = @createTree(body) 173 | 174 | if not options.strip? 175 | product-fun = (s) -> "#{name}/#{s.source}" 176 | else 177 | product-fun = (s) -> "#{name}/#{s.source.replace(options.strip, "")}" 178 | 179 | cmd-fun = (_) -> "cp #{_.source} #{_.product}" 180 | 181 | _.extend(root.model, { 182 | type: "move", 183 | options: options, 184 | destination: name, 185 | cmd-fun: cmd-fun 186 | product-fun: product-fun 187 | targetName: "m-#{uid(8)}", 188 | deps: []}) 189 | @curNode.addChild(root) 190 | 191 | collectSeq: (name, body) ~> 192 | @_collect(name, body, {+sequential}) 193 | 194 | parse: (body) ~> 195 | @curNode = @createTree(body) 196 | _.extend(@curNode.model, { targetName: "root", type: "root" }) 197 | @root = @curNode 198 | return @createTargetStore! 199 | 200 | 201 | 202 | createTargetStore: ~> 203 | iTargetStore = new targetStore() 204 | targets = _.uniq(processNodes(@root), (.name)) 205 | dirs = forAllProducts(targets, ensureDirectoryExists) 206 | sources = getSources(@root) 207 | 208 | iTargetStore.addSources getSources(@root) 209 | 210 | prepare = new phony("prepare", _.map(dirs, (.name)), {} ) 211 | 212 | removeProductCmds = forAllProducts targets, -> 213 | if not (it.name in sources) 214 | "rm -f #{it.name}" 215 | else 216 | "@echo 'Not cleaning up #{it.name} because is a source'" 217 | 218 | targets = targets ++ dirs 219 | 220 | if not (\clean in _.map(targets, (.name))) 221 | clean = new phony("clean", [] , { 222 | sequential: true 223 | actions: removeProductCmds 224 | }) 225 | targets = targets ++ [ clean ] 226 | 227 | if not (\update in _.map(targets, (.name))) 228 | update = new phony("update", [ 'clean '], { 229 | sequential: true 230 | actions: [ 231 | process.argv * ' ' 232 | ] 233 | }) 234 | targets = targets ++ [ update ] 235 | 236 | if not (\prepare in _.map(targets, (.name))) 237 | targets = targets ++ [ prepare ] 238 | 239 | _.map targets, iTargetStore.addTarget 240 | 241 | return iTargetStore 242 | 243 | addPack: (pack) ~> 244 | for k,v of pack 245 | @[k] = v.bind(@) 246 | 247 | parse = (body) -> 248 | tb = new treeBuilder() 249 | return tb.parse(body) 250 | 251 | 252 | module.exports = { 253 | build: parse 254 | parse: parse 255 | } 256 | --------------------------------------------------------------------------------