*NOT RELEASED YET - JOIN SLACK TO BE THE FIRST TO KNOW WHEN ITS OUT!* 🐊 CrocodileJS is a full-stack Node.js + Koa + ES7 MVC framework for Apps and API’s
JavaScript CSS
Latest commit 7f9bbc5 Nov 6, 2016 @g3muse g3muse committed with niftylettuce Corrected heading numbers (#229)
Permalink
Failed to load latest commit information.
.github WIP Sep 12, 2016
media More work in progress Sep 28, 2016
src Corrected heading numbers (#229) Nov 6, 2016
test WIP Sep 12, 2016
.babelrc WIP Sep 12, 2016
.crocodile.yml More work in progress Sep 28, 2016
.env.defaults More work in progress Sep 28, 2016
.env.schema More work in progress Sep 28, 2016
.eslintignore WIP Sep 12, 2016
.eslintrc WIP Sep 12, 2016
.gitignore Release 2.0.0 Jul 20, 2016
.nvmrc WIP Sep 12, 2016
COMM-LICENSE More work in progress Sep 28, 2016
LICENSE More work in progress Sep 28, 2016
Readme.md More work in progress Sep 28, 2016
gulpfile.babel.js Added more WIP (sweetalert2) Sep 15, 2016
nodemon.json Added WIP Sep 20, 2016
package.json More work in progress Sep 28, 2016

Readme.md

🐊 CrocodileJS

PayPal Donate Slack Status GPLv3 License Stability Build Status Coverage Status Standard JS Style

CrocodileJS is a Node MVC framework that lets you chew apart JavaScript.

Crocodile Dependencies

Tip: Click the crocodile 🐊 emoji in section headers to jump back to this index.

🐊 Index

🐊 What is CrocodileJS?

CrocodileJS is an application framework boilerplate for rapid iteration of a minimal viable product. It is built with ES6, ES7, Babel, and Koa, and has dozens of other extremely useful packages built-in. It is production-ready and used by several startups.

Features

  • Built for Startups, Bootstrappers, and Enterprises
  • Includes an API Server, Web Server, and Job Scheduler
  • GitHub Emojis Supported! πŸ˜„ 🐊 ✨
  • ECMAScript 7+ Features
  • Async/await callback hell
  • Koa@2.x (latest version of Koa)
  • Localization Supported with i18n
  • Basic Email Signup and Password Reset
  • API BasicAuth Access Built-in
  • 9+ Authentication Providers Supported (e.g. Google/Facebook)
  • Beautiful Email Templates and Translation Support
  • Extremely Secure and Well-tested
  • Ready for Production and Highly Configurable
  • Zero Guesswork; it uses 80+ NPM packages already hooked together

Framework Comparison

Instead of sharing a table with irrelevant benchmarks and checklists of features that only Crocodile has, we provide a simple summary of the most comparable, popular frameworks that come to mind:

  • Hackathon Starter doesn't do anything for your production-ready assets and it still uses ECMAScript 5. As a "hackathon starter", it leads you to write spaghetti code for a quick hackathon – which leads you to writing huge files that could be componentized. It also doesn't use Koa.
  • Sails was built in a way that is extremely confusing, as such (and not to be rude) but @niftylettuce won the first official SailsJS hackathon and never used SailsJS again since that one time. It locks you into their philosophy with little wiggle room due to the convoluted setup. It also uses an outdated version of Express with no plans to upgrade. It also doesn't use Koa.
  • Hapi.js simply doesn't do enough for you. Input validation and other menial features don't allow you to ship a high-quality MVP. It also doesn't use Koa.
  • Total.js was written from scratch and is against the Node philosophy... just (look at this file). It also doesn't use Koa.
  • ... honestly every single framework/boilerplate is half-baked and really not great for building stellar MVP's

There really aren't many decent Node frameworks, so only the above made the list. GitHub is full of pure web app boilerplates, but they really don't do much for you.

Thoughts

Crocodile was designed to prove the viability of an idea as quick as possible, with sheer simplicity, and an extremely ambitious quality level. It uses the latest standards, best practices, and is production ready.

For example, Crocodile can be used to release prototypes of the following in less than 30 days:

  • Enterprise Node.js Web Application for Online Ticket Booking
  • Functional Web App for Online Food Ordering
  • RESTful API for a React Native iOS/Android App
  • HR Platform for Payroll, Billing, and Employee Benefits

As you can see, quite literally any type of Software-as-a-Service business can be built with CrocodileJS! Of course, you can build something more complex or simpler than these examples with CrocodileJS.

Crocodile is more of a "boilerplate" than a "framework". It's often called a framework because of keyword search popularity and the confusion between the two words. It has a very minimal codebase, which was written with ES6/ES7 (yes, it uses the new Async/Await syntax!) – and it was structured with an MVC approach (in other words it has three folders to separate your logic src/app/models ("M" for models), src/app/controllers ("C" for controllers), and src/app/views ("V" for views).

SPA Framework

Crocodile does not come with a single page app framework out of the box, and does not use any special client-side framework by default (other than jQuery, which isn't really a SPA framework) – though it can easily be changed, and you could include React, Choo, Angular, VueJS, or whatever you'd like). The reason it does not use a SPA library, such as React or Angular, is because most (probably?) of software as a service start-ups (and API's in general) don't need something this fancy to start (API's don't even need a SPA). Using SPA frameworks will and have made proving the viability of an idea take even longer than it already should. Instead, Crocodile uses the extremely powerful and fast rendering template language called Nunjucks. In order to render it with Koa, we use koa-nunjucks-promise.

Fake Grimlock

Before diving into the structure and how it works, it is important to share that Crocodile is highly opinionated and may not be for you. If you are not comfortable or do not agree with the ideas shared in these articles, then perhaps you should not read any further into Crocodile, and close this browser tab.

  1. Rapid MVP Standards by @niftylettuce
  2. Frameworks don't make much sense by @pkrumins
  3. Do Things that Don't Scale by @paulg

Who's using CrocodileJS?

See the full list on GitHub here.

Did you ship a project with Crocodile? Post a comment here!

Architecture

Since we use ECMAScript 7+ features, Crocodile does not lock you into this framework and does not make you do something that is not ordinary. In other words, it does not make you adhere to some function named frameworkDoSomething everywhere in your app. It also does not use globals either to try to make things glue together. It's very clean and organized.

In other words, we use concepts like ES6 classes, standard import and export, and directory traversal, which are all new standards in JavaScript.

We don't use dependency injection, and quite simply don't need to. If you need to use a model in another model, you can simply import it.

The architecture is based on MVC design (another standard), and it has the following structure (tree output below):


Back-end with Koa + Async/Await

We use Koa for the back-end, and if you previously used Express, you might want to understand the differences; see this article.

To rid of callback hell, we use a new JavaScript language feature called Async/Await.

For example, here are two blocks of code that compare before and after...

This is how one might write a Mongoose save with callbacks:

// find the user that belongs to the @crocodilejs organization
// and make their user group have "admin" status
function updateUser(req, res, next) {
  Users.findOne({ org: 'crocodilejs' }, function(err, user) {
    if (err) return next(err);
    if (!user) return next(new Error('User does not exist'));
    user.group = 'admin';
    user.save(function(err, user) {
      if (err) return next(err);
      res.json(user);
    });
  });
}

This is how one might write a Mongoose save with Async/Await:

// find the user that belongs to the @crocodilejs organization
// and make their user group have "admin" status
static async updateUser(ctx) {
  let user = await User.findOne({ org: 'crocodilejs' });
  if (!user) return ctx.throw(Boom.badRequest('User does not exist'));
  user.group = 'admin';
  ctx.body = await user.save();
}

Crocodile has custom built-in error handling – so actually you don't even need to wrap the Async/Await function with a try {} catch (e) {} statement – but be careful, because in other cases you might want to (e.g. when you don't want to show a specific error from a third-party API response)!

Other scenarios where Async/Await is resourceful:

  • Populating database references cleanly
  • Complex find, search, or querying in general
  • Less indentation = more readable code
  • No more callbacks = more readable code
  • Less indentation = code wrapping to 80 or 100 (recommended) characters is easy

Front-end with jQuery + Bootstrap

By default, the latest Bootstrap version 4 alpha is used (it uses SCSS for its CSS pre-processor).

However, you can swap out Bootstrap to another version, or entirely for something else. You can also switch SCSS to use LESS or plain CSS.

Also included are the following front-end components:

You don't need Bower anymore, since you can simply import libraries through our Browserify setup!

Job Scheduler

Job scheduling is built on top of Agenda. See the src/agenda.js file and also src/jobs folder for how it works and is set up.

There are two sample jobs built-in, email and locales. The job "email" is obviously used for queuing outbound emails, and "locales" is used for generating and validating your localization efforts.

Here are a few code examples of how to queue emails:

Send an email now

import Jobs from '../../models/job';
import { Logger } from '../../../helpers';

try {
  const job = await Jobs.create({
    name: 'email',
    data: {
      template: 'some-template-name',
      to: 'niftylettuce@gmail.com',
      locals: {
        name: '@niftylettuce',
      }
    }
  });
  Logger.info('Queued an email to send now');
} catch (err) {
  Logger.error(err);
}

Send an email in 24 hours

import Jobs from '../../models/job';
import { Logger } from '../../../helpers';
import moment from 'moment';

try {
  const job = await Jobs.create({
    name: 'email',
    nextRunAt: moment().add(24, 'hours').toDate(),
    data: {
      template: 'some-template-name',
      to: 'niftylettuce@gmail.com',
      locals: {
        name: '@niftylettuce',
      }
    }
  });
  Logger.info('Queued an email to send in 24 hours');
} catch (err) {
  Logger.error(err);
}

Send an email every 5 minutes

agenda.every('5 minutes', 'five-minute-email');
getJobs() {
  return [
    [ 'email', {}, this.email ],
    [ 'locales', {}, this.locales ],
    [ 'five-minute-email', {}, this.fiveMinuteEmail ]
  ]
}

async fiveMinuteEmail(job, done) {
  try {
    const job = await Jobs.create({
      name: 'email',
      data: {
        template: 'some-template-name',
        to: 'niftylettuce@gmail.com',
        locals: {
          name: '@niftylettuce',
        }
      }
    });
    done(null, job);
  } catch (err) {
    done(err);
  }
}

You can see the contents, subjects, and templates of the provided emails in the src/emails/ folder. Emails are sent using Postmark and the package email-templates.

However in development mode, emails are not sent using an outbound service. Instead they are rendered to the OS temporary directory and opened in the browser automatically for you (saving you time and preventing you from clicking "Refresh" in Gmail a dozen times!).

Finally, here is an example of the helpful output that the "locales" job provides:

warning: the following phrases need translated in es:
Email address was invalid
Password was invalid
Reset token provided was invalid
Reset token and email were not valid together
Password strength was not strong enough
Invalid session secret
Invalid CSRF token
We have sent you an email with a link to reset your password.
Hello %s world
You have successfully registered
You have successfully reset your password.

You can specify which locales (languages) you support in the src/config/locales.js file.

Database & ORM

MongoDB and Mongoose are built in, but you can swap them out with your preferred database and ORM glue. If you need help setting up Postgres and Bookshelf, then we suggest you to change your mind! It will slow you down – but if you insist, we can put you in the right direction.

To better beautify validation error messages we use mongoose-beautiful-unique-validation

To enhance security and allow you to have selective JSON output from query results (e.g. never return a user's refresh_token, access_token, hash, or password fields) we use mongoose-json-select.

We automatically strip the default provided tokens/hash/password fields from JSON output, don't worry! See the default user model in app/models/user.js for insight.

Sessions & Auth

Redis and Passport are used for session storage and authentication.

Out of the box, we provide quite a few authentication methods (but only basic email/password and API token access is enabled by default – see Authentication Providers for more info).

NOTE: Some providers such as Twitter do not provide a user's email address. Similarly, Facebook doesn't always share a user's email, especially if they choose not to share it. For cases like this, we automatically prompt the user for a valid email address upon sign-in (if we do not detect an email address).

Type Description
Basic users sign up with an email address and password
API Token an api_token field is populated for each user in the database after their first log-in, and this can be passed as the user in user:pass with BasicAuth headers – password is blank for simplicity). This is very useful for API access if you are building an iOS/Android or client-side focused app. See the API Example below for more information on this and how to test it out.
Facebook uses passport-facebook (your users can "Log in with Facebook")
Twitter uses passport-twitter (your users can "Log in with Twitter")
Google uses passport-google-oauth (your users can "Log in with Google")
GitHub uses passport-github (your users can "Log in with GitHub")
LinkedIn uses passport-linkedin (your users can "Log in with LinkedIn")
Instagram uses passport-instagram (your users can "Log in with Instagram")
Stripe uses passport-stripe (your users can "Log in with Stripe")

If you need to add additional strategies, you can literally clone the Google Strategy code and use another passport package for your auth provider. Don't be afraid to join our Slack channel and ask us for help!

Flash Messaging

Flash Messaging

Through the use of Sweetalert2 and koa-connect-flash, we allow you to display to users extremely beautiful alerts.

ctx.flash(level, message);

Combine this with our support for international localization/translation and you get localized alerts!

The level parameter can be one of the following (adhering to Sweetalert2 defaults):

  • success - Success/OK messages

    ctx.flash('success', 'Everything worked OK');
  • info - Informational messages

    ctx.flash('info', 'You will never achieve greatness unless you believe in such greatness');
  • warning - Warning messages

    ctx.flash('warning', "Don't forget to tie your shoes before you walk forward");
  • error - Error messages

    ctx.flash('error', 'If you fail, then retry, and retry, and retry, until you succeed.');
  • question - Question messages

    ctx.flash('question', 'Who are you?');

API Example

Using the user's api_token field value (which is automatically generated upon user creation, see src/app/models/user.js), you can pass the test of the policy Policies.ensureApiToken.

For example, we have one restricted route built into Crocodile, which is GET /v1/users.

As a test, try to sign in at http://localhost:3000 after you've started the web server and API server.

Go to your account page at http://localhost:3000/my-account and copy your API token to your clipboard.

Take this value, and run the following curl command using it (replace with your API token where it says api_token below:

curl -u "api_token:" http://localhost:3000/v1/users

This should output a JSON response like the following:

{
  "id": "578ee8af0d1f58b77a4f9ad7",
  "updated_at": "2016-07-20T02:57:51.099Z",
  "created_at": "2016-07-20T02:57:51.000Z",
  "object": "user",
  "display_name": "Nick Baugh",
  "given_name": "Nick",
  "family_name": "Baugh",
  "google_profile_id": "105518868040745239689",
  "avatar_url": "https://lh3.googleusercontent.com/-XdUIqdMkCWA/AAAAAAAAAAI/AAAAAAAAAAA/4252rscbv5M/photo.jpg"
}

Security

With respect to security, we support the following out of the box:

Helpers

Crocodile uses the following helper libraries:

  • boom - HTTP-friendly error objects
  • chalk - colorful output
  • chalkline - easily debug with a huge chalkline in your terminal
  • dotenv - management of dotfiles (your environment configurations)
  • frisbee - an API wrapper for WHATWG's fetch method (similar to request!)
  • koa-convert - easily convert generator middleware functions to Async/Await
  • lodash - the utility library
  • moment - date and time formatting and manipulation
  • underscore.string - string manipulation
  • validator - string validation and sanitization

🐊 Why should I use it?

Best Practices

Latest Standards

  • Latest stable version of Node (v6.x)
  • Streams for build process with Gulp
  • Newest version of MongoDB and Redis (e.g. you can use $lookup operator with MongoDB now!)
  • Uses ES6/ES7 syntax (no more callbacks; you can use await and async finally!)
  • Latest version of Koa v2.x (or commonly referred to as @next)

LiveReload Built-in

Through koa-livereload and gulp-livereload your assets automatically reload as you continually change and save them.

This means you can have your editor open and a browser tab opened to your app at http://localhost:3000/ – of course you need to be running the task npm run watch-assets (which in turn runs the LiveReload bit) – and your changes appear in real-time! Yes, we know this is not new technology, but not many other frameworks had this built-in (at least the right way with gulp).

For example, if you make the following change to your stylesheet file and save it...

body {
-  background-color: red;
+  background-color: hotpink;
}

... then your background color on the browser tab will instantly change to hot pink.

Production Ready

Building your project for production is as easy as running npm run compile. This runs a script to compile your back-end and front-end assets consecutively.

Crocodile comes with a robust and well-tested Gulpfile (written with Babel!), check it out here. This file builds for you all the assets, from images to client-side JavaScript assets (through Browserify and Babelify).

What's a Gulpfile? You can read about Gulp here – but it's basically a file that has a series of build tasks defined (e.g. it's very similar to a Makefile or Grunt if you're familiar with that).

In a production environment, your app's assets will be minified, compressed, gzipped, revision hashed, and uploaded to Amazon S3 and CloudFront for load balancing. To do this, we use the packages koa-manifest-rev and gulp-rev, with plugins that include:

  • babelify - convert ES6/ES7 code to ES5
  • browserify - you can import (or require) modules in the browser! You no longer need to use Bower. We looked at using Webpack when we had worked on React Native stuff, but it was too complicated and we ran into way too many problems.
  • eslint - lints your JavaScript according to the .eslintrc configuration. Feel free to swap out our .eslintrc for something like eslint-config-airbnb.
  • gulp-awspublish - publishes to AWS your assets
  • gulp-cloudfront - publishes to CloudFront your assets
  • gulp-eslint - lints your JavaScript in the build runner
  • gulp-imagemin - compresses your images
  • gulp-postcss - processes your CSS
  • gulp-sourcemaps - adds sourcemaps to your code for easy debugging
  • gulp-uglify - uglifies and minifies your JavaScript code
  • imagemin-pngquant - plugin for imagemin to compress PNG's

This is the de-facto standard for hosting images, fonts, scripts, and assets in general – and it is managed properly in Crocodile using asset revisioning. For example, your file named crocodile-logo.png will become crocodile-logo-0775041dd4.png in production (you don't have to worry about cache busting!).

Not only that, but your files will be linted and they come with sourcemaps too!

To compile all the assets and source code for production:

NODE_ENV=production npm run compile

To publish to Amazon S3/CloudFront all of the assets:

NODE_ENV=production npm run publish-assets

After running these two sets of commands, you can test things out locally by running the processes (simulate production on your own machine):

Web:

NODE_ENV=production npm run app

API:

NODE_ENV=production npm run api

Job Scheduler:

NODE_ENV=production npm run agenda

NOTE: The above commands are what you'd use in a deployment configuration.

Cross-browser Compatibility

Chrome Android Safari Internet Explorer Opera Android

Is this cross-browser compatible? Yes, 100%! What about all of your ES6/ES7 code? Yes, 100%.

However, if you stick with the default v4 Bootstrap CSS framework – it only supports IE9+ and iOS 7+.

All of your code is converted to browser-friendly ES5 vanilla JavaScript (using Browserify and Babelify)...so it's cross-browser compatible!

This means both your server-side and client-side code is built to the ./lib/ folder with ES5 syntax when the Gulpfile is run with gulp build. You never have to worry about passing some --harmony flag either to run your app. The idea here is that you can focus on writing clean, short bits of code – and never have to worry about how it runs.

Built-in User Group Permissioning

By default, all users are assigned to the "user" group. This group is defined in the user model under app/models/user.js per the property 'group'`.

This approach was inspired by classic Linux-based user-group permissioning.

We've also added a default policy helper to detect whether or not a user an admin, which you can add as a middleware before your controller logic.

For example, if you want to restrict access to all /admin routes, you can add this simple middleware:

router.all('/admin*', policies.ensureAdmin);

This simple middleware is automatically added by default for you.

If you'd like to grant yourself admin access to the /admin routes, then you can run this script (replace with your email address and database name):

mongo crocodilejs_development --eval 'printjson(db.users.update({ email: "niftylettuce@gmail.com" }, { $set: { group: "admin" }}));'

Search Engine Indexing

Since we don't use a SPA, you don't have to worry about rendering SEO-friendly content! What you see in the browser is what the search engine crawler sees.

We didn't build in structured open graph tags as a default to edit, since we figure you can customize to your own needs.

With this setup, we have climbed to #1 on Google for various keywords, easily. Of course, you need great content and traffic.

In order to prevent duplicate content, we have added a plugin that removes trailing slashes from URL's, so /home/ will 301 redirect to /home automatically.

i18n Internationalization and l10n Localization

We built-in international localization/translation support!

See the following files for an understanding of how it works:

  • src/locales folder (full of all the translations, if a locale is missing, it defaults to English; en.json)
  • src/config/locales.js file (uncommented languages are the ones supported)
  • src/config/i18n.js file (see the HELLO_WORLD variable as an example – you can use the config.i18n object for error messages, success messages, page titles, page descriptions, and more)

If you want recommendations on services to use for this and how to integrate, then join our Slack and ask us!

To translate a message, you simply use the context helper method ctx.translate('SOME_CONFIG_KEY_MESSAGE'). If you need interpolation, you can pass them as such ctx.translate('SOME_MESSAGE', 'Foo', 'Bar', 'Baz') whereas config.i18n.SOME_MESSAGE = 'Hello %s %s %s' (outputs Hello Foo Bar Baz).

Translations of message keys in config.i18n are found in locales/. If you wanted to translate SOME_CONFIG_KEY_MESSAGE in Spanish, then edit the value for the key of "Hello %s %s %s" in locales/es.json.

Performance

For performance, you should always use the lean() method while writing your Mongoose queries (here's an example article showing why).

You should also never use Mongoose hooks, virtuals, or the populate method. Instead you should write static functions in the models, such as User.doSomething(user, fn). For insight as to how to write static methods, see the Mongoose docs here.

If you need to asynchronously populate or waterfall chain population of database references, we suggest to use the $lookup operator or use Async/Await syntax to keep it clean. For an example of how to write a populate() method with $lookup, see this GitHub issue.

We've also included these libraries to help with performance:

  • koa-compress - compress responses with zlib
  • koa-conditional-get - allows conditional GET response (returns a 304 "Not Modified" header if condition matches)
  • koa-etag - reduce bandwidth consumption with e-tags on responses
  • koa-response-time - adds a new response header called X-Response-Time with the number of milliseconds the server took to respond

Other than that, you just need to increase your server storage/bandwidth/memory/swap, add load balancing, and/or use PM2's clustering mode – and you should be able to support thousands of users. Horizontal scaling!

Check out the deployment section below for how a production environment and deployment process should be configured.

🐊 How do I use it?

Requirements

You'll need to have the following installed on your operating system (we provide instructions below):

  • Node >= v6.x (we recommend using NVM to manage your Node versions)
  • MongoDB >= v3.x
  • Redis >= v3.x

We also recommend that you install our preferred tools as well!

Mac OS X

  1. Install Brew:

    /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
  2. Install NVM:

    curl -o- https://raw.githubusercontent.com/creationix/nvm/master/install.sh | bash
    source ~/.bashrc
  3. Install Node with NVM:

    nvm install stable
    nvm alias default stable
  4. Install MongoDB (and make sure you read the line about launchctl after you hit ENTER):

    brew install mongo
  5. Install Redis (and make sure you read the line about launchctl after you hit ENTER):

    brew install redis

Ubuntu

  1. Install NVM:

    curl -o- https://raw.githubusercontent.com/creationix/nvm/master/install.sh | bash
    source ~/.bashrc
  2. Install Node with NVM:

    nvm install stable
    nvm alias default stable
  3. Install MongoDB:

    sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 7F0CEB10
    echo "deb http://repo.mongodb.org/apt/ubuntu "$(lsb_release -sc)"/mongodb-org/3.0 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-3.0.list
    sudo apt-get update
    sudo apt-get install -y mongodb-org
    service mongod status
  4. Install Redis:

    sudo add-apt-repository ppa:chris-lea/redis-server
    sudo apt-get update
    sudo apt-get install redis-server
    redis-benchmark -q -n 1000 -c 10 -P 5
    source ~/.profile

Windows

We do not support Windows, so please use VirtualBox or Vagrant instead with Ubuntu or Linux Mint.

Installation

We crafted a simple command-line utility for creating new projects using Crocodile.

Simply install it, then run one of the available commands.

npm install -g crocodile
crocodile


          `.-::::-.`
      `-+ooooooooooo+/`
     :osso++//+ssooooos:.
    +s/.     -sssoo+ooosso-
   :o`      :yso++++o+ooyso
   /`      `:yo+++++ooosyys-
         -/++oo+++oooosy/...
     ./osss+++oo+oossosso/
     ``/oo+ssoosoossossss/
     ./+++ooossysso:.soo
    :ooooo/:-:--.    +oo-
    `--.             `.`


🐊  CrocodileJS is a Node MVC framework that lets you chew apart JavaScript - https://crocodilejs.com


  Usage:  [options] [command]


  Commands:

    chew <dir>  Create a new CrocodileJS project
    upgrade     Upgrade your existing CrocodileJS project
    issues      Open GitHub issues for CrocodileJS
    docs        Read CrocodileJS documentation on GitHub
    rock        I wonder what this does?

  Options:

    -h, --help     output usage information
    -V, --version  output the version number

After you run crocodile chew <dir> for the first time, you will need to follow these steps to get started:

  1. Change directories to your newly created project directory:

    cd <dir>
  2. Install NPM dependencies:

    npm install
  3. Start watching assets and scripts for changes (note that this also does the required initial build of the project to the lib folder – which has to happen in order for you to use Crocodile):

    Assets (also starts LiveReload):

    npm run watch-assets

    Scripts:

    npm run watch-scripts
  4. Start the processes (install nodemon with npm install -g nodemon):

    Web:

    nodemon lib/web

    API:

    nodemon lib/api

    Jobs:

    nodemon lib/agenda
  5. Go to http://localhost:3000 in your browser.

NOTE: In production you won't run these same commands, see Production Ready for more info.

Configuration

How does configuration work?

We have made configuration of your CrocodileJS project easy through a dotenv configuration, per the Twelve-Factors.

We use the following three packages to manage configuration:

  • dotenv-extended - allows us to craft a .env definition (otherwise known as a "schema") in a file named .env.schema
  • dotenv-mustache - allows us to use the Mustache templating language in our .env and .env.defaults confguration files
  • dotenv-parse-variables - automatically parses variable types from process.env (e.g. FOO=4 will set process.env.FOO = 4 with a Number variable type instead of a String)

Configuration is managed by the following, in order of priority:

  1. Contents of the file at src/config/index.js (reads in process.env environment variables)
  2. Contents of the files in directories under src/config/environments/ (sets defaults per environment, e.g. you can pass NODE_ENV=staging and it will load the file at /src/config/environments/staging.js)
  3. Environment variables used to override defaults or set required ones (e.g. NODE_ENV=production)
  4. Environment configuration in .env
  5. Environment configuration in .env.defaults

Precedence is taken by the environment configuration files, environment variables, then the .env file.

Basically dotenv won't set an environment variable if it already detects it was passed as an environment variable.

Take a look in the src/config folder contents and also at the default .env.example file.

We've provided a default file called .env.example, which you will need to rename to .env and customize yourself.

How do I configure my app?

Basic Configuration
  1. Copy the file .env.defaults to .env (this step is automatically done for you with the crocodile chew CLI command)
  2. Edit the .env file and make it your own (e.g. replace the default email address and app name)
Authentication Providers

Authentication is managed by src/config/index.js and src/helpers/passport.js.

You can easily toggle on and off authentication providers by modifying their values to be either true or false in your .env configuration file.

Facebook Auth

TODO

Twitter Auth

TODO

Google Auth

In order to add Google Auth to your app (so users can log in with their Google account):

  1. Go to https://console.developers.google.com – Create a project (and fill out your project information – if you need a 120x120px default image, you can use this one)
  2. Under your newly created project, go to Credentials – Create credentials – OAuth client ID – Web application
  3. Set "Authorized JavaScript origins" to http://yourdomain.com (replace with your domain) and also http://localhost:3000 (for local development)
  4. Set "Authorized redirect URIs" to http://yourdomain.com/auth/google/ok (again, replace with your domain) and also http://localhost:3000/auth/google/ok (again, for local development)
  5. Copy and paste the newly created key pair for respective properties in your .env file (example below)

    -GOOGLE_CLIENT_ID=
    +GOOGLE_CLIENT_ID=424623312719-73vn8vb4tmh8nht96q7vdbn3mc9pd63a.apps.googleusercontent.com
    -GOOGLE_CLIENT_SECRET=
    +GOOGLE_CLIENT_SECRET=Oys6WrHleTOksqXTbEY_yi07
  6. In src/config/index.js, set auth.providers.google = true to enable this authentication method.
GitHub Auth

TODO

LinkedIn Auth

TODO

Instagram Auth

TODO

Stripe Auth

TODO

Amazon S3 and CloudFront Asset Setup

In order for your assets to get properly served in a production environment, you'll need to configure AWS:

  1. Go to https://console.aws.amazon.com/iam/home#security_credential ‐ Access Keys – Create New Access Key
  2. Copy and paste the newly created key pair for respective properties in your .env file (example below)

    -AWS_IAM_KEY=
    +AWS_IAM_KEY=AKIAJMH22P6W674YFC7Q
    -AWS_IAM_SECRET=
    +AWS_IAM_SECRET=9MpR1FOXwPEtPlrlU5WbHjnz2KDcKWSUcB+C5CpS
  3. Enable your API by clicking on Overview and then clicking the Enable button

  4. Go to https://console.aws.amazon.com/s3/home – Create Bucket
  5. Create a bucket and copy/paste its name for the property in .env (example below)

    -AWS_S3_BUCKET=
    +AWS_S3_BUCKET=crocodile-development
  6. Go to https://console.aws.amazon.com/cloudfront/home – Create Distribution – Get Started

  7. Set "Origin Domain Name" equal to your S3 bucket name (their autocomplete drop-down will help you find it)
  8. Leave the remaining defaults as is (some fields might be blank, this is OK)
  9. Copy/paste the newly created Distribution ID and Domain Name for respective properties in your .env file (example below)

    -AWS_CF_DI=
    +AWS_CF_DI=E2IBEULE9QOPVE
    -AWS_CF_DOMAIN=
    +AWS_CF_DOMAIN=d36aditw73gdrz.cloudfront.net

Outbound Email Configuration

By default, in a development environment, we simply render the email in your browser. However on non-development environments we send the outbound emails through Postmark App by default (though you can swap in your own transport provider):

  1. Go to https://postmarkapp.com – Start Free Trial
  2. Create a free trial account, then click Get Started, and proceed to create a "Server" and "Sender Signature"
  3. Copy/paste the "Server API token" under "Credentials" in your .env file (example below)

    -POSTMARK_API_TOKEN=
    +POSTMARK_API_TOKEN=ac6657eb-2732-4cfd-915b-912b1b10beb1
Logging Configuration

We recommend to use Sentry for server-side and client-side logging, though you could swap in your own provider:

  1. Go to https://getsentry.com/ – Try for free
  2. Create a free account, then proceed to create a "Project"
  3. Under your project, on the left side under the "DATA" section, go to "Client Keys (DSN)", and copy the private (not public) key value for "DSN" to your clipboard.
  4. Paste this value in your .env file (example below)

    -SENTRY_DSN=
    +SENTRY_DSN=https://fde13b9ab0104zz9a157a045826fb97b:fd9a23972636435eaf4bf9a414355d9a@app.getsentry.com/87713
  5. Under your project, on the left side under the "DATA" section, go to "Client Keys (DSN)", and copy the public (not private) key value for "DSN" to your clipboard.
  6. Paste this value in your .env file (example below)

    -RAVEN_DSN=
    +RAVEN_DSN=https://fde13b9ab0994zz9a157a045826fb52b@app.getsentry.com/87713

Code Coverage Configuration

We recommend to use CodeCov for code coverage configuration, though you could swap in your own provider:

  1. Go to https://codecov.io/ – Sign up with GitHub
  2. Click on your existing project, then proceed to settings where you can copy to your clipboard your Codecov token.
  3. Paste this value in your .env file (example below)

    -CODECOV_TOKEN=
    +CODECOV_TOKEN=522d2za9-22z4-az8m-9190-215zef1qpnz1

Favicon and Touch Icon Configuration

You can customize the favicon and touch icons – just generate a new set at https://realfavicongenerator.net and overwrite the existing in the src/assets/ folder. Just make sure that the paths match up in the src/assets/browserconfig.xml and src/assets/manifest.json files.

Development

You should have Crocodile installed and configured by now.

  1. Change directories to your newly created project directory:

    cd <dir>
  2. Install NPM dependencies:

    npm install
  3. Start watching assets and scripts for changes (note that this also does the required initial build of the project to the lib folder – which has to happen in order for you to use Crocodile):

    Assets (also starts LiveReload):

    npm run watch-assets

    Scripts:

    npm run watch-scripts
  4. Start the processes (install nodemon with npm install -g nodemon):

    Web:

    nodemon lib/web

    API:

    nodemon lib/api

    Jobs:

    nodemon lib/agenda
  5. Go to http://localhost:3000 in your browser.

  6. See Deployment below for how to set up other environments, such as production.

Deployment

Automated Continuous Integration Deployment Setup

We've written a comprehensive tutorial for deployment, continuous integration, and how to automate everything – you can read the article here.

If you're just interested in what tools/services we recommend, then here is a brief list:

1 Baked into Crocodile by default – you just need to provide credentials in the configuration step
2 You can get $10 free credit for signing up by clicking our referral link above
3 We include SIGTERM listener for graceful reloading, see src/app.js for more insight
4 You can send 150,000 free credits transactional emails if you're bootstrapped and DMARC compliant
5 You can get $100 free credit for signing up by clicking our referral link above

Advice

The only way to ship code faster is to respect these three points:

  1. Use as many tools as possible to automate your workflow.
  2. Understand that good coders code, and great reuse.
  3. Avoid vim and JavaScript anti-patterns and context-switching habits in order to stay focused.

Tools

Here is a brief list of recommended tools used to ship rapidly developed MVP's:

  • Use and modify the included .github folder of GitHub contribution templates (e.g. ./github/PULL_REQUEST_TEMPLATE.md is a pull request template).
  • Use a Mac OS X (preferred), Ubuntu, or Linux Mint operating system for development. If you're on Windows, use VirtualBox to install Ubuntu or Linux Mint, or you can setup a dual-boot with them. Or you could use Vagrant.
  • Use brew to install other tools for planning and building your app (e.g. GIMP, Dropbox, Evernote, iTerm 2 Beta, google-chrome, git-extras, redis, mongodb, vim, wget, ...).
  • Use Seuss.md to plan out your thoughts in a Markdown document before writing any code (this is the "Readme First Approach" that @tj and @substack shared with me).
  • Use Sketch to design your app's screens and basic mockups.
  • Use vim as your editor to write your code – build up muscle memory by installing magnitude of plugins, which will automate your puny human mistakes. If you need inspiration, here is @niftylettuce's vim config.
  • Install an eslint plugin into your text editor so that on file save it lints your code according to the rules defined in the .eslintrc file.
  • Use LookerUpper and OctoLinker to easily lookup package documentation.
  • Use fixpack to keep your package.json file tidy by running fixpack after you alter it.
  • Instead of using npm install --save <name> to install packages, use pnpm to install them faster; pnpm install --save <name>.

🐊 Is there a book on CrocodileJS?

Yes, there is a book on CrocodileJS available! It comes with Markdown, HTML, and PDF versions, and also accompanying source code AND screencasts! The author @niftylettuce is self-publishing, and goes in-depth to show you how to build projects with CrocodileJS (using actual apps he built). The book includes a "How It's Made" for two side-projects he created using CrocodileJS.

It is available for order at https://crocodilejs.com!

🐊 Can I get help?

Join us in Slack

Join us in Slack. Still need help? File an issue.

🐊 How do I get updates?

We provide updates to this repository and a detailed changelog, which you can then add/merge into your project. The GitHub Releases Pages provides detailed documentation for every release of CrocodileJS.

The CLI tool crocodile comes with a command crocodile update. Running this command will give you the latest updates on CrocodileJS.

Also, we've built-in to CrocodileJS an update-notifier script that will let you know if updates are available (based off the version in your .crocodile.yml file in the root of your project). It checks daily for updates, and you can change the frequency of this in src/config/index.js.

Please note that after you upgrade or merge changes from a new CrocodileJS release – you will need to update the version specified in .crocodile.yml of your project.

You should also "star" and "watch" this repository on GitHub to stay up to date. It is your responsibility and duty as a CrocodileJS developer to stay updated. Lastly, it's a necessity, especially for debugging that you join us on Slack!

You can also follow us on Twitter at https://twitter.com/niftylettuce.

🐊 Who built it?

CrocodileJS was released in September 2016 by @niftylettuce.

🐊 Can we hire or partner with @niftylettuce?

I am always willing to entertain new opportunities. Please reach out to me at niftylettuce@gmail.com.

🐊 License

Please respect CrocodileJS's licensing, as we have poured many hours and long nights into building it for you. Open-source isn't free for everyone. If you have feedback or questions about the licensing information below please email niftylettuce@gmail.com.

CrocodileJS has two kinds of licenses; GPLv3 for open-source projects and MIT for commercial projects.

From the GPL FAQ:

If you release the modified version to the public in some way, the GPL requires you to make the modified source code available to the program's users, under the GPL.

This means that if your project is open source, then you must release it publicly under the GPLv3 license (e.g. on a public GitHub repository). If your project is commercial and you do not want to release it publicly, then you must purchase the MIT commercial license for a one-time fee. If it is not affordable in your current situation, then please contact us and we can try to help you. This allows you to keep the code proprietary, and use CrocodileJS to develop commercial websites, e-commerce stores, consumer applications, ...

If you wish to purchase a commercial license, please do so at https://crocodilejs.com.