Skip to content

Instantly share code, notes, and snippets.

@nickknissen
Created October 2, 2013 09:00
Show Gist options
  • Save nickknissen/6790968 to your computer and use it in GitHub Desktop.
Save nickknissen/6790968 to your computer and use it in GitHub Desktop.
'use strict';
var sassMiddleware = require('./middleware/sass-middleware');
var angularTmplMiddleware = require('./middleware/angular-template-middleware');
var getEnv = require('./utils/get-env');
var AssetRewriter = require('./utils/asset-rewriter');
var fileMatches = require('./utils/file-matches');
var LiveReloadChanges = require('./utils/live-reload-changes');
var BROWSERS = [
'PhantomJS',
'Opera',
'Firefox',
'Chrome',
'ChromeCanary'
];
var CONNECT_PORT = 9000;
var ASSET_SERVER = 'http://localhost:' + CONNECT_PORT;
module.exports = function(grunt) {
// Time grunt tasks
// - must be at the top
require('time-grunt')(grunt);
grunt.task.loadTasks('./tasks');
// Production: load grunt tasks from dependencies: {}
// Development: load grunt tasks from dependencies: {} and devDependencies: {}
if (getEnv().production) {
require('matchdep').filter('grunt-*').forEach(grunt.loadNpmTasks);
} else {
require('matchdep').filterAll('grunt-*').forEach(grunt.loadNpmTasks);
}
// configurable paths
var config = {
app: 'client/',
dist: 'public/'
};
var SASS_INCLUDE_PATHS = [config.app + 'client-assets/stylesheets'];
// exposes
// - sassToCss
// - htmlToTemlate
// - assetBuildDest
// - assetServerDest
var assetRewriter = new AssetRewriter({
src: config.app,
dest: config.dist,
server: ASSET_SERVER
});
// @return {Array} files
function getSassStyleSheets() {
return grunt.file.expand([
config.app + 'client-assets/stylesheets/shared/normalize.scss',
config.app + 'client-assets/stylesheets/shared/scaffold.scss',
config.app + 'client-assets/stylesheets/shared/forms.scss',
config.app + 'client-assets/stylesheets/shared/appify.scss',
config.app + 'client-assets/stylesheets/shared/fonts.scss',
config.app + 'client-assets/stylesheets/shared/font-icons.scss',
config.app + 'client-assets/stylesheets/components/*.scss',
config.app + 'client-assets/stylesheets/utils/dimension.scss',
config.app + 'client-assets/stylesheets/utils/layout.scss',
config.app + 'client-assets/stylesheets/utils/space.scss',
config.app + 'client-assets/stylesheets/utils/type.scss',
config.app + 'client-assets/stylesheets/utils/state.scss',
config.app + 'client-assets/stylesheets/utils/link.scss',
config.app + 'client-assets/stylesheets/utils/display.scss',
config.app + 'client-assets/stylesheets/modules/*.scss'
]);
}
function getAssetServerDestOptions() {
return {
excludeServerPath: getEnv().production || getEnv().test
};
}
// @return {Array} files
// file: ASSET_SERVER +
// filename = filename.replace(config.app, '')
// filename = filename.replace(.scss, .css)
function getCssStyleSheets() {
return getSassStyleSheets().map(function(styleSheet) {
return assetRewriter.sassToCss(
assetRewriter.assetServerDest(styleSheet, getAssetServerDestOptions())
);
});
}
// @return {Object} { fileDest: fileSrc, ... }
// fileSrc: getSassStyleSheets() item
// fileDest:
// filename = filename.replace(.scss, .css)
// filename = filename.replace(config.app, config.dist)
function getSassStyleSheetMap() {
var sassStyleSheetMap = {};
getSassStyleSheets().map(function(styleSheet) {
var dest = assetRewriter.sassToCss(
assetRewriter.assetBuildDest(styleSheet)
);
sassStyleSheetMap[dest] = styleSheet;
});
return sassStyleSheetMap;
}
// @return {Array} [files]
function getTemplates() {
return grunt.file.expand([
config.app + 'app/**/*.html'
]);
}
// @return {Array} [files]
// file: ASSET_SERVER +
// filename = filename.replace(config.app, '')
// filename = filename.replace(.html.js, .html)
function getDestTemplates() {
return getTemplates().map(function(template) {
return assetRewriter.htmlToTemlate(
assetRewriter.assetServerDest(template, getAssetServerDestOptions())
);
});
}
// @return {Object} { fileDest: fileSrc, ... }
// fileSrc: getTemplates() item
// fileDest:
// filename = filename.replace(.html.js, .html)
// filename.replace(config.app, config.dist)
function getDestTemplateMap() {
var templatesMap = {};
getTemplates().map(function(template) {
var dest = assetRewriter.htmlToTemlate(
assetRewriter.assetBuildDest(template)
);
templatesMap[dest] = template;
});
return templatesMap;
}
// @return {Array} [files]
function getVendorScripts() {
return grunt.file.expand([
config.app + 'vendor/angular/angular.js',
config.app + 'vendor/angular/angular-resource.js',
config.app + 'vendor/angular/i18n/angular-locale_en-gb.js',
config.app + 'vendor/lib/lodash.custom.js',
config.app + 'vendor/lib/eddy.js',
config.app + 'vendor/lib/redefine.js',
config.app + 'vendor/lib/*.js',
]);
}
// @return {Array} [files]
function getAppScripts() {
return grunt.file.expand([
config.app + 'app/**/*.js'
]);
}
// @return {Array} files
// file: ASSET_SERVER +
// filename = filename.replace(config.app, '')
function getDestVendorScripts() {
return getVendorScripts().map(function(script) {
return assetRewriter.assetServerDest(script, getAssetServerDestOptions());
});
}
// @return {Array} files
// file: ASSET_SERVER +
// filename = filename.replace(config.app, '')
function getDestAppScripts() {
return getAppScripts().map(function(script) {
return assetRewriter.assetServerDest(script, getAssetServerDestOptions());
});
}
// XXX this should be an async task!
//
// posts file changes to livereload
// each groups src is matched against the filepath
// if a match is found src is replaced with dest and filePathTransform called
// the resulting filepath is posted to livereload
var liveReloadChanges = new LiveReloadChanges({
matches: {
css: {
src: fileMatches.scss.match,
dest: fileMatches.css.ext
},
html: {
src: fileMatches.html.match,
dest: fileMatches.template.ext
}
},
filePathTransform: assetRewriter.assetServerDest.bind(assetRewriter)
});
// Triggered when a grunt:watch task file is ['added', 'deleted', 'changed']
grunt.event.on('watch', function(action, filepath) {
if (action === 'changed') {
liveReloadChanges.process(filepath);
}
});
// END: this should be an async task!
grunt.initConfig({
config: config,
watch: {
livereload: {
options: {
livereload: true
},
files: [
'<%= config.app %>app/**/*.js',
'<%= config.app %>vendor/**/*.js',
'<%= config.app %>configs/**/*.js',
'<%= config.app %>client-assets/images/' +
'{,*/}*.{png,jpg,jpeg,gif,webp,svg}'
],
tasks: ['template']
},
// These files are handled by grunt.event.on('watch')
// Only care about 'changed' files
manualLivereload: {
files: [
'<%= config.app %>app/**/*.html',
'<%= config.app %>client-assets/stylesheets/**/*.scss'
],
options: {
event: ['changed']
},
tasks: ['template']
}
},
open: {
server: {
url: 'http://gocardless.dev:3003'
}
},
clean: {
options: {
force: true
},
build: {
files: [{
dot: true,
src: [
'.tmp',
'<%= config.dist %>client-assets/stylesheets',
'<%= config.dist %>configs',
'<%= config.dist %>vendor',
'<%= config.dist %>app'
]
}]
},
dist: {
files: [{
src: [
'<%= config.dist %>app',
'<%= config.dist %>styles',
'<%= config.dist %>scripts',
'<%= config.dist %>client-assets',
'<%= config.dist %>configs'
]
}]
},
},
filerev: {
dist: {
src: [
'<%= config.dist %>scripts/{,*/}*.js',
'<%= config.dist %>styles/{,*/}*.css',
'<%= config.dist %>client-assets/images/' +
'{,*/}*.{png,jpg,jpeg,gif,webp,svg}',
'<%= config.dist %>client-assets/fonts/*'
]
}
},
useminPrepare: {
html: '<%= config.dist %>app-index.html',
options: {
dest: '<%= config.dist %>'
}
},
usemin: {
html: ['<%= config.dist %>app-index.html'],
css: ['<%= config.dist %>styles/{,*/}*.css'],
// js: ['<%= config.dist %>scripts/{,*/}*.js'],
options: {
assetsDirs: ['<%= config.dist %>']
}
},
imagemin: {
dist: {
files: [{
expand: true,
cwd: '<%= config.dist %>client-assets/images',
src: '{,*/}*.{png,jpg,jpeg}',
dest: '<%= config.dist %>client-assets/images'
}]
}
},
htmlmin: {
dist: {
options: {
collapseWhitespace: true,
collapseBooleanAttributes: true,
removeAttributeQuotes: true,
removeRedundantAttributes: true,
useShortDoctype: true
},
files: [{
expand: true,
cwd: '<%= config.dist %>',
src: ['app-index.html'],
dest: '<%= config.dist %>'
}]
}
},
copy: {
dist: {
files: [{
expand: true,
cwd: '<%= config.app %>',
dest: '<%= config.dist %>',
src: [
'client-assets/images/{,*/}*.{png,gif,webp,svg}',
'client-assets/fonts/*'
]
}]
},
build: {
files: [{
expand: true,
cwd: '<%= config.app %>',
dest: '<%= config.dist %>',
src: ['app/**/*.js', 'vendor/**/*.js', 'configs/**/*.js']
}]
}
},
concurrent: {
dist: [
'sass',
'ngtemplatecache',
'imagemin'
// conflicts with usemin
// 'htmlmin'
]
},
cdnify: {
dist: {
html: ['<%= config.dist %>*.html']
}
},
ngmin: {
dist: {
files: [{
expand: true,
cwd: '<%= config.dist %>scripts',
src: '*.js',
dest: '<%= config.dist %>scripts'
}]
}
},
uglify: {
options: {
mangle: false
// sourceMap: '<%= config.dist %>scripts/app-source-map.js',
// sourceMapRoot: '<%= config.dist %>scripts'
}
},
sass: {
options: {
includePaths: SASS_INCLUDE_PATHS,
outputStyle: 'compressed'
},
dist: {
files: getSassStyleSheetMap()
}
},
template: {
app: {
options: {
data: function getTemplateData() {
return {
ENV: getEnv(),
styleSheets: getCssStyleSheets(),
vendorScripts: getDestVendorScripts(),
appScripts: getDestAppScripts()
.concat(getDestTemplates())
};
}
},
files: {
'<%= config.dist %>app-index.html': [
'<%= config.app %>app-index.html.tpl'
]
}
}
},
ngtemplatecache: {
app: {
options: {
stripPrefix: config.app
},
files: getDestTemplateMap()
}
},
jshint: {
options: {
jshintrc: '.jshintrc'
},
all: [
'./Gruntfile.js',
'<%= config.app %>/app/**/*.js'
]
},
plato: {
app: {
options : {
jshint : grunt.file.readJSON('.jshintrc')
},
files: {
'reports': ['<%= config.app %>/app/**/*.js']
}
}
},
karma: {
options: {
configFile: '<%= config.app %>/spec/config/karma-unit.conf.js'
},
watch: {
options: {
autoWatch: true,
singleRun: false
}
},
ci: {
options: {
browsers: ['Chrome', 'Firefox'],
reporters: ['junit']
}
},
unit: {
options: {
browsers: BROWSERS
}
},
coverage: {
options: {
reporters: ['coverage'],
preprocessors: {
'**/*.html': ['ng-html2js'],
'app/**/*.js': ['coverage']
}
}
}
},
connect: {
options: {
port: CONNECT_PORT
},
// For debugging the integration test server
debug: {
options: {
keepalive: true,
port: 4004,
middleware: require('./client/spec/config/protractor.server.js')
}
},
server: {
options: {
middleware: function (connect) {
return [
connect.static(config.app),
sassMiddleware({
root: config.app,
includePaths: SASS_INCLUDE_PATHS,
sourceComments: 'map',
outputStyle: 'expanded'
}),
angularTmplMiddleware({
root: config.app,
stripPrefix: config.app
})
];
}
}
}
},
protractor: {
options: {
configFile: '<%= config.app %>/spec/config/protractor.conf.js'
}
},
lodash: {
build: {
dest: 'app/client/vendor/lodash.custom.js',
options: {
modifier: 'modern',
// Async methods mess with angulars event loop
// use angular $timeout/$q
minus: ['debounce', 'throttle', 'defer', 'delay']
}
}
},
env: {
server: {
NODE_ENV: 'development',
},
build: {
NODE_ENV: 'production',
},
test: {
NODE_ENV: 'test',
}
},
'ddescribe-iit': {
files: [
'<%= config.app %>/spec/**/*.js',
'!<%= config.app %>/spec/vendor/**/*.js',
'!<%= config.app %>/spec/config/**/*.js'
]
}
});
//////////////////////////////////////////////////////////////////////////////
grunt.registerTask('test:integration', [
'env:test',
'dist',
'protractor',
'clean',
// Restore dev:
'env:server',
'template'
]);
grunt.registerTask('test', [
'jshint',
'test:integration',
'karma:unit'
]);
grunt.registerTask('citest', [
'ddescribe-iit',
'jshint',
'test:integration',
'karma:ci'
]);
grunt.registerTask('server', [
'env:server',
'connect:server',
'template',
'watch'
]);
grunt.registerTask('dist', [
'clean',
'copy:build',
'template',
'useminPrepare',
'concurrent:dist',
'concat',
'copy:dist',
'ngmin',
'uglify',
'cssmin',
'filerev',
'usemin',
'clean:build'
]);
grunt.registerTask('build', [
'env:build',
'dist'
]);
};
{
"name": "gocardless-dashboard",
"version": "2.0.0",
"homepage": "http://dashboard.gocardless.com/",
"repository": {
"type": "git",
"url": "git://github.com/gocardless/dashboard.git"
},
"bugs": {
"url": "http://github.com/gocardless/dashboard/issues"
},
"dependencies": {
"matchdep": "~0.1.2",
"node-sass": "~0.6.4",
"q": "~0.9.6",
"glob": "~3.2.3",
"grunt-cli": "~0.1.9",
"grunt": "~0.4.1",
"grunt-usemin": "git+ssh://git@github.com:yeoman/grunt-usemin.git#v2.0",
"grunt-filerev": "~0.1.0",
"grunt-concurrent": "~0.3.0",
"grunt-sass": "~0.6.1",
"grunt-google-cdn": "~0.2.0",
"grunt-ngmin": "~0.0.3",
"grunt-lodash": "~0.2.0",
"grunt-template": "~0.2.0",
"grunt-contrib-copy": "~0.4.1",
"grunt-contrib-concat": "~0.3.0",
"grunt-contrib-uglify": "~0.2.2",
"grunt-contrib-cssmin": "~0.6.1",
"grunt-contrib-htmlmin": "~0.1.3",
"grunt-contrib-imagemin": "~0.1.4",
"grunt-contrib-clean": "~0.5.0",
"grunt-env": "~0.4.0",
"time-grunt": "~0.1.1"
},
"devDependencies": {
"karma": "~0.10.0",
"karma-ng-html2js-preprocessor": "~0.1.0",
"karma-coverage": "~0.1.0",
"karma-jasmine": "~0.1.0",
"karma-chrome-launcher": "~0.1.0",
"karma-firefox-launcher": "~0.1.0",
"karma-phantomjs-launcher": "~0.1.0",
"karma-sauce-launcher": "~0.1.0",
"karma-opera-launcher": "~0.1.0",
"karma-growl-reporter": "~0.1.0",
"karma-junit-reporter": "~0.1.0",
"protractor": "~0.7.0",
"Faker": "~0.5.11",
"connect": "~2.8.4",
"grunt-open": "~0.2.1",
"grunt-karma": "~0.7.0",
"grunt-plato": "~0.2.1",
"grunt-contrib-connect": "~0.3.0",
"grunt-contrib-watch": "~0.5.1",
"grunt-contrib-jshint": "~0.6.2",
"grunt-contrib-csslint": "~0.1.2",
"grunt-ddescribe-iit": "~0.0.2"
},
"scripts": {
"postinstall": "node ./script/setup-dev-env",
"test": "grunt citest"
},
"engines": {
"node": "0.10.0"
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment