{{description}}
This environment is intended to be used in a modular way. Everything is a Component and should work and be testable independently.
Generated with the Grail Generator for Yeoman.
Task management is done with Gulp, so your project is easily extendible. This stack already uses some Gulp Plugins:
- gulp-util -
noop()
and logging - gulp-concat - To concat files together
- gulp-imagemin - Wrapper for imagemin, to optimize images
- gulp-stylus - Wrapper for Stylus, to compile to CSS
- gulp-autoprefixer - Wrapper for Autoprefixer, to vendor prefix CSS3
- gulp-csso - Wrapper for CSSO, to minify CSS
- gulp-uglify - Wrapper for Uglify, to minify JavaScript
- gulp-spawn-mocha - To run tests with Mocha
- gulp-bump - To bump up the version
number in
package.json
Codes are written mainly in CoffeeScript and bundled together with Browserify. Out of the box it comes with some transforms for Browserify:
- coffeeify - To compile CoffeeScript to JavaScript
- html2js-browserify - To compile HTML templates to JavaScript strings
- debowerify - To use Bower components in Browserify
- deamdify - To use AMD modules in Browserify
- require-globify - To require whole directories with globs
Styles are written mainly in Stylus.
Watching for file changes is crucial if you pre-compile the Scripts and Styles. For the Script re-bundle Watchify is in charge.
Since the Styles are not in the JS bundle,
Chokidar is used to watch and rebuild
them. gulp.watch
isn't used here, since it does not pick up newly created
files.
For the rest of the files (HTML, Images) it uses gulp.watch.
The generated and bundled files will be served with BrowserSync. That means the Application is automatically reloaded if Scripts or Styles are changing.
Component tests written in CoffeeScript, bundled by Browserify and run by Mocha.
Source of all tasks is ./client
and destination is ./public
.
Copies the HTML Entry Point.
Copies fonts. This tasks copies the fonts of Font Awesome included by
Semantic-UI by default, but note that you need to run
yo grail:extend
, so the files are
available.
Bundles the Style Entry Point with Stylus. Also prefix CSS3 properties with Autoprefixer.
Copies all images.
Bundles the Script Entry Point with Browserify. It executes several transforms:
- coffeeify - transforms the CoffeeScript to JavaScript
- html2js - transforms the HTML templates to JavaScript strings
- debowerify -
require()
modules installed by Bower - deamdify -
require()
modules that are wrapped by AMD
Watches files in the source directory and re-bundles the Script Entry Point.
- Run
browserify-watch
- Run
stylus
whenever a./client/**/*.styl
changes - Run
images
whenever a file in./client/images/*
changes - Run
html
whenever a./client/*.html
changes
Starts a BrowserSync web server. Also starts the watch
task. Whenever a file
in the source directory changes, the Script or Style Entry Point is re-bundled
and the browser reloaded. Styles are directly injected in the page without a
reload.
Bundles the Application Components and Tests together and run them in Mocha.
Whenever a Application or Test file changes re-run the test
Task.
Run browserify
, stylus
, images
and html
. gulp
without a task name will
also run this task.
Run this task if you want to release the Application for the audience. Note that
you need to run gulp build
before!
- Run
browserify
and minify JS with Uglify - Run
stylus
and minify CSS with CSSO - Run
images
and minify them with Imagemin
Bumps up the version number in package.json
.
This is a one page application. It has three different entry points.
This is your typical index.html
page which is loaded first. It has references
to the Script and Stylesheet entry points.
Markup that is initially loaded (the Layout), is stored here.
Here you initialize components. Since the Script Bundle is packed
with Browserify you can simply require()
the components you want to use:
Post = require('./components/post')
post = new Post
It is recommended to require()
third party dependencies in the components
rather than having globals. But if you install the libraries with Bower, the
respective global will be defined without declaring it:
require('jQuery') # $ is globally defined
However, if you declare it this way your tests may brake because they are run in Node.js by default (todo: run them in PhantomJS).
Here you initialize stylesheets, third party stylesheets and variables. Stylus
will take care of the bundling, so all you need is @import
.
For third party styles, include a .css
file in a relative path:
@import '../bower_components/normalize-css/normalize.css'
To import a module style:
@import './components/post/style'
Note that you don't need to include every single Component style since by
default every style.styl
from the ./client/components
is imported.
If you define variables, you can use them in your imported components:
backgroundColor = #eee
And in ./client/components/post/style.styl
:
@import './colors'
body
background: backgroundColor
A component can have three different things: A Script, a Template, and a Style. All three are optional, since it's in your hands how you initialize each part of a component.
Again, you can name them like you want, really. I recommend this structure:
./client/components/[component-name]
/index.coffee
/template.html
/style.styl
Why this naming? You can leave out the file extension when require()
ing them
(which would not possible if every component-part has the same name):
post = require('../post') # respectively index.coffee
template = require('./template') # respectively template.html
With the component-name
as identifier, and index.coffee
as Script entry
point, you describe with the filename which part of the component you want to
require()
: post
(respectively index.coffee
), post/template
and
post/style
.
A disadvantage of this method is that it might not be clear if you are editing multiple components in your editor. But you should work on one component at a time anyway.
You can and should test your Components. Just like the Application Script Entry
Point, the Tests have a Entry Point too in ./test/client/index.coffee
. There
you require()
the components and tests:
postTest = require('./components/post')
Then just create the component test a file named like the component:
./test/client/components/post.coffee
:
should = require('should')
Post = require('../../../client/components/post')
post = new Post
describe 'Post Component', ->
it 'should have the correct template', ->
post.template.should.equal 'testing'
To bundle the Application and Tests, and run it in Mocha:
gulp test
To automatically re-run the test
task whenever Application or Test files
change, run:
gulp test-watch
Third party libraries can be either installed with NPM or Bower. In the end, you
just need to require()
the library.
npm install moment --save
bower install zepto --save
Then you can require the libraries wherever you want (usually in the Component):
$ = require('zepto')
moment = require('moment')
In the rare case you can't install a library from NPM or Bower, you also can
require()
the .js
from everywhere:
lib = require('../path/to/lib.js')
Install it via Bower:
bower install foundation --save
And @import
it in ./client/index.styl
:
@import '../bower_components/foundation/css/foundation.css'