Skip to content

Instantly share code, notes, and snippets.

@joshdmiller
Last active December 12, 2015 07:58
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save joshdmiller/4740743 to your computer and use it in GitHub Desktop.
Save joshdmiller/4740743 to your computer and use it in GitHub Desktop.
A Grunt 0.4 build system for modularized AngularJS projects.

And your file tree looks like so:

Gruntfile.js
build/
  |-- grunt-html2js.js
  |-- grunt-testacular.js
src/
  |-- index.html
  |-- app/
  |   |-- app.js
  |   |-- myDataService.js
  |   |-- myDataService.spec.js
  |-- components/
  |   |-- componentOne/
  |   |   |-- componentOne.js
  |   |   |-- componentOne.spec.js
  |   |-- componentTwo/
  |   |   |-- componentTwo.js
  |   |   |-- componentTwo.spec.js
/**
* Based on html2js by the AngularUI/bootstrap team: https://angular-ui.github.com/bootstrap
* Updated to Grunt ~0.4.0 by Josh David Miller
*
* Licensed MIT
*/
module.exports = function (grunt) {
// HTML-2-JS Templates
var path = require('path'),
TPL = 'angular.module("<%= id %>", []).run(["$templateCache", function($templateCache) {\n $templateCache.put("<%= id %>",\n "<%= content %>");\n}]);\n',
templateModule = "angular.module('templates', [<%= templates %>]);\n",
escapeContent = function(content) {
return content.replace(/"/g, '\\"').replace(/\r?\n/g, '" +\n "');
},
normalizePath = function(p) {
if ( path.sep !== '/' ) {
p = p.replace(/\\/g, '/');
}
return p;
};
grunt.registerTask('html2js', 'Generate js version of html template.', function() {
this.requiresConfig('html2js.src');
var files = grunt.file.expand( grunt.config('html2js.src') ),
base = grunt.config('html2js.base') || '.',
dest = grunt.config('html2js.dest') || '.',
templates = [];
files.forEach(function(file) {
var id = normalizePath( path.relative(base, file) );
templates.push("'" + id + "'");
grunt.file.write( path.resolve( dest, id + '.js' ), grunt.template.process( TPL, {
data: {
id: id,
content: escapeContent( grunt.file.read( file ) )
}
}));
});
grunt.file.write( path.resolve( dest,'templates.js' ), grunt.template.process( templateModule, {
data: {
templates: templates.join(', ')
}
}));
});
};
/**
* Based on testacular by the AngularUI/bootstrap team: https://angular-ui.github.com/bootstrap
* Updated to Grunt ~0.4.0 by Josh David Miller
*
* Licensed MIT
*/
module.exports = function ( grunt ) {
var testacularCmd = process.platform === 'win32' ? 'testacular.cmd' : 'testacular';
// TODO migrate this entirely to Grunt
function runTestacular( testConfigFile, options ) {
var args = [ 'start', testConfigFile, '--reporters=dots', '--colors' ].concat( options ),
done = grunt.task.current.async(),
child = grunt.util.spawn({
cmd: testacularCmd,
args: args
}, function testacularError( err, result, code ) {
grunt.log.writeln("Running cmd");
if ( code ) {
done( false );
} else {
done();
}
});
child.stdout.pipe(process.stdout);
child.stderr.pipe(process.stderr);
};
grunt.registerTask( 'test-watch', 'watch file changes and test', function gruntTestWatchTask() {
var options = [ '--auto-watch', '--no-single-run' ];
runTestacular( 'config/testacular-unit.js', options );
});
grunt.registerTask( 'test', 'run testacular unit tests', function gruntUnitTestTask() {
var options = [ '--single-run', '--no-auto-watch' ];
runTestacular( 'config/testacular-unit.js', options );
});
grunt.registerTask( 'e2e', 'run testacular E2E tests', function gruntE2eTestTask() {
var options = [ '--single-run', '--no-auto-watch' ];
runTestacular( 'config/testacular-e2e.js', options );
});
};
/**
* A Boilerplate Grunt ~0.4.0 build system for AngularJS projects
*
* Licensed MIT
*/
module.exports = function ( grunt ) {
// Load required Grunt tasks
grunt.loadNpmTasks('grunt-recess');
grunt.loadNpmTasks('grunt-contrib-clean');
grunt.loadNpmTasks('grunt-contrib-copy');
grunt.loadNpmTasks('grunt-contrib-jshint');
grunt.loadNpmTasks('grunt-contrib-concat');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadTasks('build');
// Project Configuration
grunt.initConfig({
distdir: 'dist',
pkg: grunt.file.readJSON("package.json"),
meta: {
banner:
'/**\n' +
' * <%= pkg.title || pkg.name %> - v<%= pkg.version %> - <%= grunt.template.today("yyyy-mm-dd") %>\n' +
' * <%= pkg.homepage %>\n' +
' *\n' +
' * Copyright (c) <%= grunt.template.today("yyyy") %> <%= pkg.author %>\n' +
' * Licensed <%= pkg.licenses.type %> <<%= pkg.licenses.url %>>\n' +
' */\n'
},
src: {
js: [ 'src/**/*.js', '!src/**/*.spec.js', '<%= distdir %>/tmp/**/*.js' ],
atpl: [ 'src/app/**/*.tpl.html' ],
ctpl: [ 'src/components/**/*.tpl.html' ],
html: [ 'src/index.html' ],
less: 'src/less/main.less'
},
test: {
unit: [ 'src/**/*.spec.js' ]
},
clean: [ '<%= distdir %>' ],
copy: {
assets: {
files: [
{
src: [ '**' ],
dest: '<%= distdir %>/assets/',
cwd: 'src/assets',
expand: true
}
]
},
data: {
files: [
{ src: [ 'config.json' ], dest: '<%= distdir %>/data/' }
]
}
},
concat: {
options: {
banner: '<%= meta.banner %>'
},
dist: {
src: [ '<%= src.js %>' ],
dest: '<%= distdir %>/assets/<%= pkg.name %>.js'
},
libs: {
src: [
'vendor/angular/angular.js'
],
dest: '<%= distdir %>/assets/libs.js'
}
},
recess: {
build: {
src: [ '<%= src.less %>' ],
dest: '<%= distdir %>/assets/<%= pkg.name %>.css',
options: {
compile: true
}
}
},
jshint: {
all: [ 'Gruntfile.js', '<%= src.js %>', '<%= test.unit %>' ],
options: {
curly: true,
immed: true,
newcap: true,
noarg: true,
sub: true,
boss: true,
eqnull: true
},
globals: {}
},
html2js: {
app: {
src: [ '<%= src.atpl %>' ],
base: 'src/app',
dest: 'dist/tmp'
},
component: {
src: [ '<%= src.ctpl %>' ],
base: 'src/components',
dest: 'dist/tmp'
}
},
watch: {
files: [ '<%= src.atpl %>', '<%= src.ctpl %>', '<%= src.js %>',
'<%= src.html %>', 'src/**/*.less', '<%= test.unit %>',
'src/assets/**/*' ],
tasks: [ 'default', 'timestamp' ]
}
});
// The default task
grunt.registerTask( 'default', [ 'build' ] );
grunt.registerTask( 'build', ['clean', 'html2js', 'jshint', 'test', 'concat', 'recess', 'index', 'copy'] );
// Compile the index.html template
grunt.registerTask( 'index', 'Process index.html template', function () {
grunt.file.copy('src/index.html', 'dist/index.html', {process:grunt.template.process});
});
// Print a timestamp (useful for when watching)
grunt.registerTask( 'timestamp', function() {
grunt.log.subhead(Date());
});
};
<!DOCTYPE html>
<html ng-app="app" ng-controller="AppCtrl">
<head>
<title>My AngularJS Page</title>
<link rel="stylesheet" type="text/css" href="assets/<%= grunt.config.get('pkg.name') %>.css"/>
<script type="text/javascript" src="assets/libs.js"></script>
<script type="text/javascript" src="assets/<%= grunt.config.get('pkg.name') %>.js"></script>
</head>
<body>
<h1>Yada yada</h1>
</body>
</html>
{
"author": "Josh David Miller",
"name": "example.com",
"version": "0.0.1-SNAPSHOT",
"homepage": "http://example.com",
"licenses": {
"type": "GPL",
"url": "http://www.gnu.org/licenses/gpl.html"
},
"bugs": "http://...",
"repository": {
"type": "git",
"url": "git@github.com: ..."
},
"dependencies": {},
"devDependencies": {
"grunt": "~0.4.0rc7",
"grunt-recess": "~0.3.0",
"grunt-contrib-clean": "~0.4.0rc6",
"grunt-contrib-copy": "~0.4.0rc7",
"grunt-contrib-jshint": "~0.1.0",
"grunt-contrib-concat": "~0.1.2rc6",
"grunt-contrib-watch": "~0.2.0rc7"
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment