Giter Site home page Giter Site logo

pliers's Introduction

pliers - A buildy, watchy type tool

build status

Installation

  npm install -g pliers

Introduction

Pliers allows you to use JavaScript to write your build tasks like you would your applications. It has three key features include/exclude filesets, dependency resolution, and file watching.

FAQ

Why bother making a new build tool, what is wrong with make?

make is an great tool, but sometimes you need to do more that just run scripts and create folders. Sometimes it is handy to have a little project context when doing build tasks. pliers is all JavaScript so you can use your existing code and npm modules.

Why not just make a node.js script for build tasks that need then and call them from make?

That is a good method and will work for many projects. But you are splitting an activity over two languages as soon there is a little bit of complexity it makes maintenance, debugging and knowledge transfer harder. Having a structured build system with a minimal but useful feature set certainly solves problems for us at Clock.

Is there a plugin system?

Yes it's called require()

CLI Usage

Usage: pliers [options] [task]

Options:
  -h, --help                                          output usage information
  -V, --version                                       output the version number
  -t, --tasks [file]                                  A file with user defined tasks (Default: ./pliers.js)
  -l, --list                                          List all available tasks with descriptions
  -b, --bare                                          List task names only
  -a, --all                                           Run all named tasks with in the current tree
  -L, --logLevel [trace|debug|info|warn|error|fatal]  Set the level of logs to output

pliers.js

Running pliers will look for a pliers.js in the current working directory.

Tasks

module.exports = function (pliers) {

  pliers('hello', function (done) {
    pliers.logger.info('Hello world')
    done()
  })

}

To run the hello task from the command line:

 pliers hello

Dependencies

Pliers will resolve and run all dependencies before executing the task

pliers('test', function (done) {
  pliers.exec('npm test', done)
})

pliers('lint', { description: 'Run jshint all on project JavaScript' }, function (done) {
  pliers.exec('jshint lib test', done)
})

pliers('qa', 'test', 'lint')

This will run test task and then the lint task.

 pliers qa

API

Pliers is not very opinionated and has very little API surface area. That said there are a few built in functions.

exec(command)

Executes command using require('child_process').spawn and returns the ChildProcess.

pliers('list', function (done) {
  pliers.exec('ls', done)
})

run(taskName)

Run another pliers task.

pliers('runner', function (done) {
  pliers.run('list', done)
})

load(folder)

Load another pliers project into a parent. This is useful if you have standalone sub projects.

pliers.load('./subproject')

You can then run sub project tasks from the parent using the -A option.

runAll(taskName)

Run all pliers task for any loaded sub pliers project.

pliers('build', function (done) {
  pliers.runAll('build', done)
})
 pliers build

This will build all the sub project build tasks

filesets(id, includePatterns[, excludePatterns])

Create a fileset that can be used to perform tasks on. The following fileset example would return all .js files in the current directory, excluding those that end in .test.js.

pliers.filesets('js', __dirname + '/*.js', __dirname + '/*.test.js')

includePatterns & excludePatterns can be either a string or an Array if you need multiple glob conditions.

Filesets are calculated using the node-glob module. The filesets are first generated when they are accessed, this is done using the id property as follows:

console.log(pliers.filesets.js) // Will output the fileset with the id 'js'

watch()

// Run the unit tests whenever a JavaScript file changes
pliers('watchCss', function (done) {

  pliers.filesets('js', __dirname + '/*.js', __dirname + '/*.test.js')

  pliers('test', function (done) {
    pliers.exec('npm test', done)
  })

  pliers.watch(pliers.filesets.js, function() {
    pliers.run('test')
  })
})

Credits

Licence

Licensed under the New BSD License

pliers's People

Contributors

bengourley avatar evlun avatar tomgco avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

pliers's Issues

renderCss is called twice, with timeout between the two

The below example comprises of an example where updating one Stylus file (one character & saving) runs the renderCss() twice.

Extract from pliers log:

13:05:48 [pliers] Changed: public/css/components/study.styl (change)
13:05:48 [pliers] Running task: renderCss
13:05:54 [pliers] Changed: public/css/components/study.styl (change)
13:05:54 [pliers] Running task: renderCss
13:05:54 [pliers] Completed task: renderCss

Extracts from ClockSite's site/pliers.js:

pliers.filesets('css', join(__dirname, 'public', '**/*.styl'))

pliers('renderCss', function (done) {

  function compile(str, path) {
    return stylus(str)
      .use(nib())
      .set('filename', path)
      .set('warn', false)
      .set('compress', true)
      .define('versionPath', function (urlPath) {
        return new stylus.nodes.Literal('url(' + mappedVersion.versionPath(urlPath.val) + ')')
      })
  }
  var mappedVersion = versionator.createMapped(require(__dirname + '/static-file-map.json'))
    , stylesheets =
    [ join(__dirname, 'public', 'css', 'index.styl')
    , join(__dirname, 'public', 'css', 'oldie.styl')
    ]

  stylusRender(stylesheets, { compile: compile }, function (err) {
    if (err) pliers.logger.error(err.message)
    done()
  }).on('log', log)
})

pliers.watch(pliers.filesets.css, function () {
  pliers.run('renderCss', function () {
    notify('CSS rendered')
    refresh('css/index.css')
  })
})

pliers-cli should be a module that loads whatever version of pliers is installed in node_modules

The current setup generally relies on pliers being installed globally, but projects that use pliers in npm scripts use locally installed versions. This can lead confusion when there is a mismatch, and also a pain when pliers tasks make use of new pliers features, or a new release of pliers has fixed a bug that the user is not aware of.

I think we should (as grunt does, and I'm sure other cli tools do) create a new module called pliers-cli which loads in whatever version of pliers that is in node_modules. That way the correct version is always used. It also takes the burden off of users to update pliers for when a project uses new features or when a bug fix is made.

Anything thrown is caught during pliers.js load

Pliers loads the user's pliers.js file within a try/catch. This is so that the nice error message below can be output if a pliers.js (or differently specified file) doesn't exist:

Could not load 'pliers.js'

However, this catches anything which may get thrown when the module is loaded. This needs to be changed so that it checks the specifics of the error, and decide to rethrow or not.

Possible issue with logging

When watching a fileset, calling a second pliers.run after the primary task seems to screw with the logging.

screen shot 2015-07-03 at 10 28 26

screen shot 2015-07-03 at 10 25 43
screen shot 2015-07-03 at 10 32 46

I'm working on a minimal project, with minimal restart time, so it's not to do with saving files too quickly while tasks are still happening. Timestamps in the screenshots show ~10s between saves.

The server remains responsive, even after letting this happen multiple times, and @microadam has had a very brief look to check I'm not doing anything too strange. This has us thinking it's just an issue with logging, not that tasks are hanging in strange states etc.

zsh plugin install on npm install

Making an issue for this rather than just comments in the actual plugin pull request.

@serby, do you have a recommendation on this? I'm thinking checking in oh-my-zsh is in the users directory, then symlinking it into the custom plugins folder, outputting instructions on enabling it (this is how I've added mine). If it's not, can just output what to add to the .zshrc.

Browser refresh

Auto refresh the browser after files get compiled.
grunt-reload http://github.com/webxl/grunt-reload uses a reverse proxy to inject javascript into the page and then uses websockets to tell the page to reload. Maybe we could get something similar in pliers

Return the child() from `pliers.exec`

Trying to achieve nodemon-style behaviour but there is no way of killing a child process. Returning the child from pliers.exec() allows us to call child.kill([signal]).

Tasks that forget to callback close the event loop

This code illustrates the problem:

pliers('a', function (done) {
  // Don't call done()
})

pliers.run('a', function () {
  console.log('a is done')
})

Obviously, since task 'a' doesn't call done(), 'a is done' does not get output. Further, since task 'a' doesn't do anything with done and just returns, nothing gets added to the event loop, and execution finishes.

This is a problem if tasks depend on 'a', and expect them to callback, eg:

pliers('b', 'a', function () {
  console.log('a completed, now do b stuff')
})

The behaviour I would expect from the task runner is that the execution hangs until the callback is called. The current behaviour can mislead into thinking that everything ran fine. I've taken a look at mocha, which has a similar interface:

it('should call the callback', function (done) {
  console.log('not gonna call')
})

When this is run with mocha, it hangs for 2 seconds then outputs an error message:

Error: timeout of 2000ms exceeded

The expected behaviour in mocha is implemented with timeouts:
https://github.com/visionmedia/mocha/blob/master/lib/runnable.js#L164

I think we should implement a similar system.

New feature: Ability to refresh filesets

Simplified possible use case: A fileset containing all images in a project is defined which will be passed to an optimisation task. During build another tasks creates some additional images which too need to be optimised. If these images didn't exist when the fileset was created they would be ignored by the optimisation task.

There should only ever be one instance of a task running at any one time. This can happen via watch()

If you are watching for files to change to restart a server. After the initial watch being triggered and while the server is being restarted you can change a file and another restart will be kicked off mid restart.

What should happen is that when a task is running, it is marked as blocked and any subsequent calls get queued. Then at the end of the restart if there are any items in the queue the task clears them and does one more run. This would stop tasks getting clobbered when doing switching branches.

OSX Notification Center Integration

I imagine this functionality wouldn't go into pliers itself but I'm just using this repo to add the feature request.

It would be cool for tasks to hook in to the notification center to alert when they are done, to save flicking back and forth to the console.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.