Joel Kemp
JavaScript engineer at Bēhance.
Joel Kemp, @mrjoelkemp
Bēhance/Adobe
grunt-contrib-sass
sass: {
// 1. SASS compile configuration
compile: {
files: {
// 2. Turn globbing on
expand: true,
// 3. All sass files – no matter how deep
src: ['**/*.scss'],
// 4. Where to store the compiled output
dest: 'css/'
}
}
}
module.exports = function(grunt) {
grunt.initConfig({
// 1. Compile all scss files
sass: {
compile: {
files: {
expand: true,
src: ['**/*.scss'],
dest: 'css/' }
}
},
watch: {
// 2. Watch all scss files and run the sass task
sass: {
files: ['**/*.scss'],
tasks: ['sass']
}
}
});
// 3. Load plugins
grunt.loadNpmTasks('grunt-contrib-sass');
grunt.loadNpmTasks('grunt-contrib-watch');
};
YA!
Like YA!
// Custom callback for the watch event
grunt.event.on('watch', function(action, filepath) {
var ext = path.extname(filepath);
// Tell YA about added file extensions
if (action === 'added') {
process.send('EXTADDED:' + ext);
}
});
{
// 1. Grunt plugin to install
lib: 'grunt-contrib-sass',
target: {
// 2. Compile task configuration
sass: {
compile: {
files: [{
expand: true,
src: ['**/*.{scss, sass}',
ext: '.css'
}]
}
}
}
}
{
lib: 'grunt-contrib-coffee',
target: {
// Coffee compile target
coffee: {
compile: {
files: [{
expand: true,
// All coffeescript files
src: ['**/*.coffee'],
ext: '.js'
}]
}
}
}
}
More at: http://bit.ly/hijack-grunt
grunt.initConfig({
// 1. Compile all scss files (simplified)
"sass": {
"compile": {
"files": [{ "src": ["**/*.{scss, sass}"] }]
}
},
"coffee": {
"compile": {
"files": [{ "src": ["**/*.coffee"] }]
}
},
"watch": {
// 2. Watch all scss files and run the sass task on change
"sass": {
"files": [
"**/*.scss",
],
"tasks": ["sass"]
},
// CoffeeScript watch task is very similar...
}
});
Check out the code: http://bit.ly/dynamic-sass
"requirejs": {
"bundle": {
"options": {
// 1. What's the entry point?
"include": "app.js",
// 2. What should we call the generated bundle?
"out": "output.js"
}
}
}
// Custom callback for the watch event
grunt.event.on('watch', function(action, filepath) {
var ext = path.extname(filepath);
if (action === 'added') {
process.send('EXTADDED:' + ext);
}
});
Static analysis with Esprima
define([
'./a'
], function(a) {
'use strict';
});
"expression": {
"type": "CallExpression",
"callee": {
"type": "Identifier",
"name": "define"
},
"arguments": [{
"type": "ArrayExpression",
"elements": [{
"type": "Literal",
"value": "./a",
"raw": "'./a'"
}]
}]
}
JavaScript File
Abstract Syntax Tree (AST)
Full AST: bit.ly/full-ast
It's just JSON
esprima.parse(code)
Esprima is used in:
JSHint
JSCS
JSDoc
and ~533 more!
var isAMD = hasDefine || hasAMDTopLevelRequire;
// 1. hasDefine
define('foo', [
'./a'
], function(a) {
});
// 5. hasTopLevelRequire
require([
'./a'
], function(a) {
});
// 6. hasExports
module.exports = function() {
};
// 8. hasRequire && !hasDefine
var a = require('a');
// 2. hasDefine
define([
'./a'
], function(a) {
});
// 3. hasDefine
define(function(require) {
var a = require('./a');
});
// 4. hasDefine
define({
});
// 7. hasExports
exports.foo = function() {
};
var isCommonJS = hasExports || (hasRequire && ! hasDefine);
define([
'./a'
], function(a) {
'use strict';
});
// node
"expression": {
// 1. node.type
"type": "CallExpression",
"callee": {
// 2. node.callee.type
"type": "Identifier",
// 3. node.callee.name
"name": "define"
},
"arguments": [{
"type": "ArrayExpression",
"elements": [{
"type": "Literal",
"value": "./a",
"raw": "'./a'"
}]
}]
}
// Is the current node an AMD define() call?
function isDefine(node) {
return node.type === 'CallExpression' &&
node.callee.type === 'Identifier' &&
node.callee.name === 'define';
}
substack/detective
mrjoelkemp/detective-amd
mrjoelkemp/node-app-root
grunt.initConfig({
// 1. Bundle task
"requirejs": {
// 2. Auto-generated target name
"t0": {
"options": {
// 3. The determined root
"include": "js/amd/a2.js",
// 4. Auto-generated output bundle name
"out": "a2-r-bundle.js"
}
}
},
"watch": {
// 4. Watch all "relevant" JS files and rebundle on change
"requirejs": {
// Avoid watching 3rd party libraries
// and the bundles themselves (infinite loop)
"files": ["**/*.js", allTheExceptions],
"tasks": ["requirejs"]
}
}
});
Take the grunt-work out of using Grunt.
Joel Kemp, @mrjoelkemp
Bēhance/Adobe
By Joel Kemp
Slides for my talk at the Powered by JavaScript conference at Strangeloop 2014