Skip to content

Commit

Permalink
tasks now cascade its return value to the next task in a series
Browse files Browse the repository at this point in the history
  • Loading branch information
Jorge Bucaran committed Jul 5, 2015
1 parent 8a8c01b commit 213915e
Show file tree
Hide file tree
Showing 3 changed files with 104 additions and 110 deletions.
85 changes: 44 additions & 41 deletions src/fly.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ import * as _ from "./util"

export default class Fly extends Emitter {
/**
Create a new instance of Fly. Use fly.start(...) to run tasks.
@param {Object} Flyfile, also known as host
@param {String} relative base path / root
@param {Array} list of plugins to load.
* Create a new instance of Fly. Use fly.start(...) to run tasks.
* @param {Object} Flyfile, also known as host
* @param {String} relative base path / root
* @param {Array} list of plugins to load.
*/
constructor ({ host, root = "./", plugins = [] }) {
super()
Expand All @@ -28,24 +28,24 @@ export default class Fly extends Emitter {
plugins.forEach((plugin) => plugin.call(this))
}
/**
Log a message with a time stamp as defined in /fmt.
* Log a message with a time stamp as defined in /fmt.
*/
log (...args) {
_.log(`[${fmt.time}] ${args}`)
return this
}
/**
Clear each specified directory. Wraps rimraf.
@param {...String} paths
* Clear each specified directory. Wraps rimraf.
* @param {...String} paths
*/
clear (...paths) {
const _clear = this.defer(rimraf)
return paths.map((path) => _clear(path))
}
/**
Concatenates files read via source.
@param {String} name of the concatenated file
@TODO: by default this operation should clear the target file to concat
* Concatenates files read via source.
* @param {String} name of the concatenated file
* @TODO: by default this operation should clear the target file to concat
*/
concat (name) {
this._writers.push(({ dest, data }) => {
Expand All @@ -55,13 +55,12 @@ export default class Fly extends Emitter {
return this
}
/**
Add a filter to be applied when unwrapping the source promises.
@param {
{String} name of the filter.
{Object} { filter, options } object.
{Function} filter function.
}
@param [{Function}] filter function.
* Add a filter to be applied when unwrapping the source promises.
* @param
* {String} name of the filter.
* {Object} { filter, options } object.
* {Function} filter function.
* @param [{Function}] filter function.
*/
filter (name, filter) {
if (name instanceof Function) {
Expand All @@ -76,48 +75,52 @@ export default class Fly extends Emitter {
return this
}
/**
Watch for changes on globs and run each of the specified tasks.
@param {Array:String} glob patterns to observe for changes
@param {Array:String} list of tasks to run on changes
* Watch for changes on globs and run each of the specified tasks.
* @param {Array:String} glob patterns to observe for changes
* @param {Array:String} list of tasks to run on changes
*/
watch (globs, tasks) {
this.notify("fly_watch").start(tasks)
_.watch(globs, { ignoreInitial: true }).on("all", () => this.start(tasks))
.then(() => _.watch(globs, { ignoreInitial: true })
.on("all", () => this.start(tasks)))
return this
}

/**
Runs the specified tasks.
@param {Array} list of tasks to run
* Runs the specified tasks. This method can be yielded inside a task.
* The return value of each task (after the first) is passed on to the
* next task in the series.
* @param {Array} list of tasks to run
* @return {Promise}
*/
start (tasks = []) {
/** @deprecated default tasks will revert to `default`in 0.2.0 */
if (tasks.length === 0) tasks.push(this.host.default ? "default" : "main")
co.call(this, function* () {
for (let task of [].concat(tasks)
return co.call(this, function* (ret) {
for (const task of []
.concat(tasks)
.filter((task) => {
return ~Object.keys(this.host).indexOf(task)
|| !this.notify("task_not_found", { task })
})) {
const start = new Date()
this.notify("task_start", { task })
try {
yield this.tasks[task].call(this)
ret = yield this.tasks[task].call(this, ret)
this.notify("task_complete", {
task, duration: (new Date()).getTime() - start
})
} catch (error) {
this.notify("task_error", { task, error })
}
} catch (error) { this.notify("task_error", { task, error }) }
}
})
return this
}
/**
Creates an array of promises with read sources from a list of globs.
When a promise resolved, the data source is reduced applying each of
the existing filters.
@param {...String} glob patterns
@return Fly instance. Promises resolve to { file, data }
*/
* Creates an array of promises with read sources from a list of globs.
* When a promise resolved, the data source is reduced applying each of
* the existing filters.
* @param {...String} glob patterns
* @return Fly instance. Promises resolve to { file, data }
*/
source (...globs) {
this._source = []
this._filters = []
Expand All @@ -143,18 +146,18 @@ export default class Fly extends Emitter {
return this
}
/**
Resolves an array of promises an return a new promise with the result.
By default resolves the current promise sources.
@param {Array:Promise} source
* Resolve an array of promises an return a new promise with the result.
* By default resolves the current promise sources.
* @param {Array:Promise} source
*/
unwrap (source = this._source) {
return new Promise((resolve) => {
Promise.all(source).then((result) => resolve.apply(this, result))
})
}
/**
Resolves all source promises and writes to each destination path.
@param {...String} destination paths
* Resolves all source promises and writes to each destination path.
* @param {...String} destination paths
*/
target (...dest) {
return Promise.all(_.flatten(dest).map((dest) => {
Expand Down
23 changes: 12 additions & 11 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1,25 +1,26 @@
import Parsec from "parsec"
import { notifyUpdates, findFlypath as find } from "./util"
import { notifyUpdates as notify, findFlypath as find } from "./util"
import reporter from "./reporter"
import cli from "./cli/"
import pkg from "../package"
import go from "co"

export default function* () {
notifyUpdates({ pkg })
let { help, list, file, version, _: tasks } =
Parsec.parse(process.argv)
.options("file", { default: "./" })
.options("list")
.options("help")
.options("version")

let { help, list, file, version, _: tasks } =
Parsec.parse(process.argv)
.options("file", { default: "./" })
.options("list")
.options("help")
.options("version")
notify({ pkg })

export default go(function* () {
if (help) {
cli.help()
} else if (version) {
cli.version(pkg)
} else {
const path = yield find({ file })
const path = yield find(file)
if (list) {
cli.list(path, { simple: list === "simple" })
} else {
Expand All @@ -29,4 +30,4 @@ export default function* () {
.start(tasks)
}
}
}
})
106 changes: 48 additions & 58 deletions src/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,12 @@ import { join } from "path"
import { jsVariants } from "interpret"
import updateNotifier from "update-notifier"

/** console.log wrapper */
export function log (...args) {
console.log.apply(console, args)
}

/** console.error wrapper */
export function error (...args) {
console.error.apply(console, args)
}
export const log = console.log.bind(console)
export const error = console.error.bind(console)

/**
Pretty print error.
@param {Object}
* Wrapper for prettyjson and other stack tracing improvements.
* @param {Object} error object
*/
export function trace (e) {
error(pretty.render(e)
Expand All @@ -31,9 +24,9 @@ export function trace (e) {
}

/**
Promisify an async function.
@param {Function} async function to promisify
@return {Function} function that returns a promise
* Promisify an async function.
* @param {Function} async function to promisify
* @return {Function} function that returns a promise
*/
export function defer (asyncFunc) {
return (...args) => new Promise((resolve, reject) =>
Expand All @@ -42,19 +35,19 @@ export function defer (asyncFunc) {
}

/**
Flattens a nested array recursively.
@return [[a],[b],[c]] -> [a,b,c]
* Flatten a nested array recursively.
* @return [[a],[b],[c]] -> [a,b,c]
*/
export function flatten (array) {
return array.reduce((flat, next) =>
flat.concat(Array.isArray(next) ? flatten(next) : next), [])
}

/**
Search `fly-*` plugins listed in package.json dependencies.
@param {Package} project's package.json
@param {Array} blacklisted plugins
@return {Array} list of loadable fly deps
* Search `fly-*` plugins listed in package.json dependencies.
* @param {Package} project's package.json
* @param {Array} blacklisted plugins
* @return {Array} list of loadable fly deps
*/
export function searchPlugins ({ pkg, blacklist = []}) {
if (!pkg) return []
Expand All @@ -66,63 +59,62 @@ export function searchPlugins ({ pkg, blacklist = []}) {
}

/**
Expand a glob pattern and runs a handler for each expanded glob.
@param pattern {String} Pattern to be matched
@param handler {Function} Function to run for each unwrapped glob promise.
@return {Promise}
* Expand a glob pattern and runs a handler for each expanded glob.
* @param pattern {String} Pattern to be matched
* @param handler {Function} Function to run for each unwrapped glob promise.
* @return {Promise}
*/
export function expand (pattern, handler) {
return new Promise((resolve, reject) => {
glob(pattern, {}, (error, files) =>
error
? reject(error)
: Promise.all(handler(files)).then((files) =>
resolve(files)).catch((error) => { throw error }))
: Promise.all(handler(files))
.then((files) => resolve(files))
.catch((error) => { throw error }))
})
}

/**
Wrapper for chokidar.watch. Array of globs are flattened.
@param {Array:String} globs
@param {...String} tasks Tasks to run
@return {chokidar.FSWatcher}
* Wrapper for chokidar.watch. Array of globs are flattened.
* @param {Array:String} globs to watch
* @param {Object} chokidar options
* @return {chokidar.FSWatcher}
*/
export function watch (globs, opts) {
return chokidar.watch(flatten([globs]), opts)
export function watch (globs, options) {
return chokidar.watch(flatten([globs]), options)
}

/**
Wrapper for update-notifier.
@param {Array} options
* Wrap update-notifier notify.
* @param {Array} options
*/
export function notifyUpdates (options) {
updateNotifier(options).notify()
}

/**
Resolve the Flyfile path. Check the file extension and attempt to load
every possible JavaScript variant if `file` is a directory.
@param {String} file or path to the Flyfile
@param [{String}] Flyfile variant name
@return {String} path to the Flyfile
* Resolve the Flyfile path. Check the file extension and attempt to load
* every possible JavaScript variant if file is a directory.
* @param String file or path to the Flyfile
* @param String:Array Flyfile variant name
* @return {String} path to the Flyfile
*/
export function* findFlypath ({ file, names = ["Flyfile", "Flypath"] }) {
const root = join(process.cwd(), file)
return hook(require, (yield fs.stat(file)).isDirectory()
export function* findFlypath (path, names = ["Flyfile", "Flypath"]) {
const root = join(process.cwd(), path)
return hook(require, (yield fs.stat(path)).isDirectory()
? yield resolve(match(
names.concat(names.map((name) => name.toLowerCase()))
.map((name) => join(root, name)),
Object.keys(jsVariants)
))
: root)

/**
Add require hook so that subsequent calls to require transform the
JavaScript source variant (ES7, CoffeeScript, etc.) in the fly.
@param {Function} require function to load selected module
@param {String} path to Flyfile
@return {String} path to Flyfile
@private
* Add require hook so that subsequent calls to require transform the
* JavaScript source variant (ES7, CoffeeScript, etc.) in the fly.
* @param {Function} require function to load selected module
* @param {String} path to Flyfile
* @return {String} path to Flyfile
*/
function hook (require, path) {
const js = jsVariants[`.${path.split(".").slice(1).join(".") || "js"}`]
Expand All @@ -140,10 +132,9 @@ export function* findFlypath ({ file, names = ["Flyfile", "Flypath"] }) {
}

/**
Resolve to the first existing file in paths.
@param {Array:String} list of paths to search
@return {String} path of an existing file
@private
* Find the first existing file in paths.
* @param {Array:String} list of paths to search
* @return {String} path of an existing file
*/
function* resolve (paths) {
if (paths.length === 0) throw { code: "ENOENT" }
Expand All @@ -153,11 +144,10 @@ export function* findFlypath ({ file, names = ["Flyfile", "Flypath"] }) {
}

/**
Match files and extensions.
@param {Array:String} List of files to match
@param {Array:String} List of extensions to match
@return {Array} Product of matched files * extensions
@private
* Match files and extensions.
* @param {Array:String} List of files to match
* @param {Array:String} List of extensions to match
* @return {Array} Product of matched files * extensions
*/
function match (files, exts) {
return files.length === 1
Expand Down

0 comments on commit 213915e

Please sign in to comment.