Skip to content

criteo/loop

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

46 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

loop

Build Status npm version Dependency Status

Awesome dev loop — enhance your web application development workflow

Why?

Working on today's web applications requires managing several build systems. For example you will use sbt, cargo, cake, go build or maven to compile your backend; webpack, grunt or gulp to build your frontend; npm or bower to fetch some dependencies; plus a bunch of sql or bash scripts to reset your data...oh, and you have to restart your server after a change if it does not auto-reload for you.

If you are not using a full-stack integrated web framework that manages all that for you, your development workflow (thus your productivity) probably suffers a lot.

loop enhances your workflow by managing all these repetitive steps. It sits in front of your application, and runs all the needed build tasks before starting your server. Then it monitors the filesystem changes, rebuilding and restarting if needed.

Because it is external to your project, it is agnostic to your technology choices: you can use whatever build system and setup you prefer. It just runs your build tasks and monitors the results.

And because everything happens in your browser, you can stay focused on your work: edit your source code, click reload in your browser and see the result!

Usage

loop is a command line tool that needs to be installed from npm.

Install globally

$ npm install --global devloop

And then you can use it in any project, by running the loop command.

Install locally to a project

If you do not want to install it globally, you can also install a specific version in your project dependencies.

$ npm install --save-dev devloop

And then use it by running the node_modules/.bin/loop command.

Run the dev loop

Run the loop command in any looped project, ie. a project that contains a devloop.js definition file (see the docs). This file defines the tasks needed to run the project in development mode.

$ cd my_looped_webapp/
$ loop
=> Now browse to http://localhost:8080

You can then point your browser to http://localhost:8080. It will run the build and stream the result to the browser. Once the build is done, your server is started and your application is served.

If you have changed some files in your project, just reload the page in your browser. The necessary build step will be run again.

Pressing r in the terminal while loop is running will force the server task to restart. Pressing x will kill the current build and restart from scratch.

Docs

The development tasks are described in a file named devloop.js located at your project root. This is a javascript file, declaring several "tasks" and their dependencies. There are two mandatory tasks: proxy, and server.

Therefore, a minimal devloop.js file looks like:

'use strict'

let server = runServer({
  httpPort,
  sh: `python -m SimpleHTTPServer ${httpPort}`
})

proxy(server, 8080)

It starts the reverse proxy on port 8080, and runs the server task before serving the application.

Any task can also declare further dependencies, for example:

proxy(server, 8080).dependsOn(webpack, less)

In this case the proxy will start serving the application when the server is ready, and the webpackand less tasks have completed as well.

Finally, tasks can watch the filesystem, and restart themselves if something changes. Of course, if a task restarts, all its dependencies will restart as well:

let server = runServer({
  httpPort,
  sh: `java -cp "lib/*" MyServer`,
  watch: 'lib/**'
})

Here, as soon as a jar file change in the lib directory, the server will be marked dirty, and will restart at the next page reload. The httpPort variable here is injected to the main scope by loop, and is set to a random, available TCP port. You are free to use it or not.

proxy(serverTask, httpPort)

Starts a reverse proxy (RP) at httpPort. The RP will start serving the application as soon the serverTask and all its dependencies are ready.

run(options)

Runs a task. A task can be any process that can be forked. loop waits for the exit status of the process, and decides whether the task was successful or not.

Options:

  • name: String, the task label in the UI.
  • sh: String, the shell command to fork (requires Linux, MacOS, or running on Cygwin).
  • command: Array of String, the command + options to fork (if not using sh).
  • cwd: String, the current directory for the task.
  • env: Object, the environment for the task.
  • watch: String or Array of String, the file patterns to watch.
runServer(options)

Starts a server task. A server task is different from a standard task because loop does not wait for the process to exit to mark it as successful. Instead, it waits for the http port opened by the server process to be available.

Options:

  • name: String, the task label in the UI.
  • httpPort: Number, the http port on which the server should listen.
  • sh: String, the shell command to fork (requires Linux, MacOS, or running on Cygwin).
  • command: Array of String, the command + options to fork (if not using sh).
  • cwd: String, the current directory for the task.
  • env: Object, the environment for the task.
  • watch: String or Array of String, the file patterns to watch.
maven(options)

Basically the same as using run({sh: 'maven'}) but colorizes the Maven output (the only advantage of using this instead of a plain run task).

Options:

  • name: String, the task label in the UI.
  • sh: String, the shell command to fork (requires Linux, MacOS, or running on Cygwin).
  • command: Array of String, the command + options to fork (if not using sh).
  • cwd: String, the current directory for the task.
  • env: Object, the environment for the task.
  • watch: String or Array of String, the file patterns to watch.
startSbt(options)

Starts a resident sbt build. This task returns an sbt session that you can use to run further sbt commands. The main advantage of using this instead of the run task to run sbt directly, is that the sbt session is kept alive so running the sbt commands is then way faster. Especially for scalac commands.

Options:

  • name: String, the task label in the UI.
  • sh: String, the shell command to fork (requires Linux, MacOS, or running on Cygwin).
  • command: Array of String, the command + options to fork (if not using sh).
  • cwd: String, the current directory for the task.
  • env: Object, the environment for the task.
  • watch: String or Array of String, the file patterns to watch.

The returned sbt session allows can then create more sbt tasks using run with these options:

  • name: String, the task label in the UI.
  • command: String, the sbt command to run.
webpack(options)

Start a resident webpack compiler and use it to build your project. It will use the webpack module installed in your project under node_modules/webpack. It is way faster to use this dedicated task than forking a new webpack command at each build.

  • name: String, the task label in th UI (default to webpack).
  • config: String, name of the webpack configuration file (default to webpack.config.js).
  • watch: String or Array of String, the file patterns to watch.

Examples

Some example configurations are available in the examples/ directory:

  • dummy-project: The 'Hello world' for loop; just shows the basics.
  • node-project: A sample node project, with some frontend assets build with webpack and stylus.
  • java-project: A sample java project, with some frontend assets build with webpack and stylus.

FAQ

Will loop replace my build system?

Not at all, loop is not another build system, it will just run your build systems for you. Think about it as a bot that will run all the commands you usually run yourself in a terminal while you are developing.

I use loop, but the build step is too long. My dev workflow was not improved :(

You probably need to split down your build pipeline into smaller parts. Actually, a dev build pipeline is generally different than the one you use to build the distributed version of your project. It does not mean that you have to duplicate your build; you just have to compose it from smaller parts.

When loop restarts my server, I'm logged out/my application crashes

Yes, if you use a server that does not support automatic code reload, loop will have to restart the server process. But in a well designed application it should not log you out. There are several ways to store the session state outside of the server process (either on the client side or in a datastore which is not tied to the actual server code version). Yes, it will help your development process, but it will also help you at deployment time if you want to run your code on several application servers.

License

This project is licensed under the Apache 2.0 license.

Copyright

Copyright © Criteo, 2017.