bailey.js

bailey.js is a pragmatic language that compiles to javascript. Sort of like coffeescript, but a lot more inspired by python than ruby, with a focus on creating a cleaner, saner language with less flaws.

View it on Github

Installation

Assuming you already have node.js, installation is as easy as:

$ npm install bailey -g

This installs it globally, but of course you can put it in your project’s package.json manually or by running npm install bailey --save.

Usage

In the command line

bailey sourceDir/ targetDir/

Converts all .bs files in sourceDir to a .js file in targetDir.

In Javascript

There are two possible ways to utilize bailey in Javascript. The first is to compile a string using parseString or compile the content of a folder like the command line example by using parseFiles.

var bailey = require('bailey');
var compiledString = bailey.parseString(sourceString, {});

// This is the equivalent of the command line example
bailey.parseFiles('sourceDir/', 'targetDir/', {}});

Options

The options listed below can be used in different settings. Prepended with -- is used with the command line and the ones in the parentheses are used in the option parameter when running bailey from javascript.

--node (node)

Use node imports instead of requirejs-imports, for running things server-side.

--remove-comments (removeComments)

Remove all comments in the compiled version.

--bare (bare)

Make the Javascript file without the wrapper function.

--watch

Watch the source file or directory, recompiling when any file changes.

--version

Output the current version.

Configurationfile .baileyrc

Bailey.js has support for configuration through a json file called .baileyrc. It can be used in order to shorten the parse command or to define several configurations for a project. This can often be the use case in an web project with a node or io.js backend. The configuration file can define multiple sets of options like the example below. Any options passed in the cli will override the configuration file and if positional arguments are passed in the cli bailey will not read the configuration file.

{
  "server": {
    "source": "src/server",
    "target": "dist/server",
    "node": true
  },
  "frontend": {
    "source": "src/frontend",
    "target": "dist/server/public"
  }
}

The example above will compile the server code as node and the frontend code as require.js when you run bailey. It is also possible to run bailey -c frontend or bailey --config frontend in order to compile only the frontend code.

Imports

Bailey.js

import bailey
import bailey as bs
import gulp-bailey as gulpBailey
import bailey: parseString, parseFiles

Javascript

Regular imports
define(["bailey", "bailey", "gulp-bailey", "bailey"], function(bailey, bs, gulpBailey, bailey) {
    var parseString = bailey.parseString;
    var parseFiles = bailey.parseFiles;
}
Node imports
var bailey = require('bailey');
var bs = require('bailey');
var gulpBailey = require("gulp-bailey");
var parseString = require('bailey').parseString;
var parseFiles = require('bailey').parseFiles;

Exporting

Bailey.js

export bailey

Javascript

Regular
define([], function() {
    "use strict";

    return bailey;
});
Node
module.exports = bailey;

Datatypes

Bailey.js

number = 42
decimal = 3.14159265359

yeah = true
hellNo = false

# Strings are as you expect, plus string interpolation
string = 'Hi!'
anotherString = "The answer is #{number}"
stringWithMultipleLines = "This
is cool"

# Objects can be with commas...
obj = {
    number: 42,
    string: "Wee"
}

# ...or without
flavors = {
    vanilla: true
    chocolate: false
}

listOfStuff = [0,2,4,6]
anotherList = [
    1,
    3,
    5
]

# Functions are very coffeelike
totallyRandomNumber = () ->
    return 4 # guaranteed to be random

Javascript

var number = 42;
var decimal = 3.14159265359;

var yeah = true;
var hellNo = false;

// Strings are as you expect, plus string interpolation
var string = 'Hi!';
var anotherString = "The answer is " + number + "";
var stringWithMultipleLines = "This\nis cool";

// Objects can be with commas...
var obj = {
    'number': 42,
    'string': "Wee"
};

// ...or without
var flavors = {
    'vanilla': true,
    'chocolate': false
};

var listOfStuff = [0, 2, 4, 6];
var anotherList = [1, 3, 5];

// Functions are very coffeelike
var totallyRandomNumber = function totallyRandomNumber() {
    return 4;
};

Comments

Bailey.js

# This is an awesome comment

Javascript

// This is an awesome comment

If-statements

Bailey.js

if 'chocolate' in apartment
    console.log("Awesome!")
else
    console.log("Too bad for you.")

if numberOfIceCreamsToday == 0
    console.log("Go eat ice-cream")
elif numberOfIceCreamsToday > 2
    console.log("Good!")

    if numberOfIceCreamsToday > 5
        console.log("Serously?")

if 'chocolate' in apartment then console.log("Awesome!")

Javascript

if ('chocolate' in apartment) {
    console.log("Awesome!");
} else {
    console.log("Too bad for you.");
}

if (numberOfIceCreamsToday === 0) {
    console.log("Go eat ice-cream");
} else if (numberOfIceCreamsToday > 2) {
    console.log("Good!");

    if (numberOfIceCreamsToday > 5) {
        console.log("Serously?");
    }
}

if ('chocolate' in apartment) {
    console.log("Awesome!")
}

Loops

Bailey.js

for item in bagOfDelicousCandy
    console.log(item)

for number in 0:10:2
    console.log(number)

for value of IceCreamObject
    console.log(value)

for key, value of IceCreamObject
    console.log('#{key}: #{value}')

chocolateCount = 4
while chocolateCount > 0
    console.log('Eat chocolate')
    chocolateCount -= 1

Javascript

var __l1 = bagOfDelicousCandy.length;
for (var __i1 = 0; __i1 < __l1; __i1++) {
    var item = bagOfDelicousCandy[__i1];
    console.log(item);
}

for (var number = 0; number < 10; number = number + 2) {
    console.log(number);
}

for (var __i2 in IceCreamObject) {
    var value = IceCreamObject[__i2];
    console.log(value);
}

for (var key in IceCreamObject) {
    var value = IceCreamObject[key];
    console.log('' + key + ': ' + value + '');
}

var chocolateCount = 4;
while (chocolateCount > 0) {
    console.log('Eat chocolate');
    chocolateCount -= 1;
}

Classes

Bailey.js

class Candy
    title: ""
    slug: ""
    price: 0
    quantity: 0

    init: (title, price, quantity) ->
        @title = title
        @price = price
        @quantity = quantity
        @slug = slugify(title)

    calculatePrice: (quantity) ->
        return quantity * @price

class Chocolate extends Candy
    types: ['dark', 'milk', 'white']
    type: 0
    chocolateDiscount: 0.8

    init: (title, price, quantity, type) ->
        super.init(title, price, quantity)
        @type = type

    calculatePrice: (quantity) ->
        return super.calculatePrice(quantity) * @chocolateDiscount

Javascript

function Candy(title, price, quantity) {
    if ((typeof window !== "undefined" && this === window) || (typeof self !== "undefined" && this === self)) {
        throw new TypeError("Tried to call class Candy as a regular function. Classes can only be called with the 'new' keyword.");
    }
    this.title = title;
    this.price = price;
    this.quantity = quantity;
    this.slug = slugify(title);
}
Candy.prototype.title = "";
Candy.prototype.slug = "";
Candy.prototype.price = 0;
Candy.prototype.quantity = 0;
Candy.prototype.calculatePrice = function calculatePrice(quantity) {
    return quantity * this.price;
};

function Chocolate(title, price, quantity, type) {
    if ((typeof window !== "undefined" && this === window) || (typeof self !== "undefined" && this === self)) {
        throw new TypeError("Tried to call class Chocolate as a regular function. Classes can only be called with the 'new' keyword.");
    }
    Candy.call(this, title, price, quantity);
    this.type = type;
}
Chocolate.prototype = Object.create(Candy.prototype);
Chocolate.prototype.types = ['dark', 'milk', 'white'];
Chocolate.prototype.type = 0;
Chocolate.prototype.chocolateDiscount = 0.8;
Chocolate.prototype.calculatePrice = function calculatePrice(quantity) {
    return Candy.prototype.calculatePrice.call(this, quantity) * this.chocolateDiscount;
};

Scope

Bailey.js

@sent = false

request(url, (err, res, body) ->
    if not err
        # we can reach an outer scope by adding a @
        # for each level out that we need to go
        @@sent = true
)

Javascript

var __scope_4__ = this;
this.sent = false;

request(url, function(err, res, body) {
    if (!err) {
        // we can reach an outer scope by adding a @
        // for each level out that we need to go
        __scope_4__.sent = true;
    }
});

Regex

Bailey.js

/\d+/.test('We have 20 different sorts of IceCream')

Javascript

/\d+/.test('We have 20 different sorts of IceCream');

Try it yourself

...and watch it appear here as javascript

Tools

Feedback?

Feedback is awesome! Did you find a bug, do you have a question or do you have a great idea for a feature? Just open an issue on Github or join our discussion on gitter.im/haeric/bailey.js