Web Standards Days, 24 ноября 2012 г., Москва
Автоматизируют подготовку проекта к боевому окружению:
Сборка JS, CSS, картинок и прочего фронтенда — забота технолога.
<project name="Grunt vs. Ant" default="min" basedir=".">
<property name="JS" value="src/*.js" />
<property name="BUILD" value="build/scripts.js" />
<property name="BUILD_MIN" value="build/scripts.min.js" />
<target name="concatenate" description="Concatenate JavaScript files">
<echo message="Concatenation..."/>
<concat destfile="${BUILD}">
<fileset dir="." includes="${JS}"/>
</concat>
</target>
<target name="min" depends="concatenate" description="Minify JS...">
<echo message="Minification..."/>
<exec executable="uglifyjs">
<arg line="-o ${BUILD_MIN} ${BUILD}"/>
</exec>
</target>
</project>
JS = src/*.js
BUILD = ./build/scripts.js
BUILD_MIN = ./build/scripts.min.js
all: min
concat:
mkdir -p `dirname $(BUILD)`
cat $(JS) > $(BUILD)
min: concat
uglifyjs -o $(BUILD_MIN) $(BUILD)
module.exports = function(grunt) {
grunt.initConfig({
concat: {
main: {
src: 'src/*.js',
dest: 'build/scripts.js'
}
},
min: {
main: {
src: '<%= concat.main.dest %>',
dest: 'build/scripts.min.js'
}
}
});
grunt.registerTask('default', 'concat min');
};
module.exports = function(grunt) {
grunt.initConfig({
/* ... */
});
};
module.exports = function(grunt) {
grunt.initConfig({
concat: {
}
});
grunt.registerTask('default', 'concat');
};
concat: {
main: {
},
second: {
}
}
concat: {
main: {
src: 'js/*.js',
dest: 'build/scripts.js'
}
}
'js/main.js'
[ 'js/utils.js', 'js/main.js' ]
[ 'js/libs/*.js', 'js/mylibs/**/*.js' ]
*
— любые символы./**/
— папка любой вложенности.
concat: { main: {
src: [ 'js/utils.js', 'js/main.js' ],
dest: 'build/scripts.js'
}},
min: { main: {
src: '<%= concat.main.dest %>',
dst: 'build.<%= grunt.template.today("m-d-yyyy") %>.js'
}}
meta: {
banner: '/* © Ivan Pupkin */'
},
min: { main: {
src: [ '<banner>', 'build/build.js' ], ...
}}
$ npm install grunt-stylus
module.exports = function(grunt) {
grunt.initConfig({
stylus: { ... }
});
grunt.loadNpmTasks('grunt-stylus');
grunt.registerTask('default', 'stylus');
};
$ grunt
$ grunt concat
$ grunt concat:main
$ grunt --debug
$ grunt.cmd # Windows :-)
concat: { main: { ... dest: 'build/scripts.js' }},
min: { main: { src: '<%= concat.main.dest %>',
dest: '<%= concat.main.dest %>' }}
...
grunt.registerTask('default', 'concat min');
grunt.registerTask('debug', 'concat');
$ grunt
или $ grunt debug --debug
Будем запускать из Гранта imgo —
консольный оптимизатор веб-графики.
См. также grunt-imgo, grunt-exec.
imgo: {
images: {
files: 'images/**'
}
}
grunt.registerMultiTask('imgo', '…', function() {
// this.data.files ===
// <%= imgo.images.files %>
});
var done = this.async();
var files = grunt.file.expandFiles(this.data.files);
grunt.utils.async.forEach(files,
function(file, next) {
// Обрабатываем каждый файл
}
, done);
function(file, next) {
grunt.utils.spawn({
cmd: 'imgo',
args: [file]
}, next);
}
module.exports = function(grunt) {
grunt.initConfig({
imgo: {...}
});
grunt.registerMultiTask('imgo', ...);
grunt.registerTask('default', 'imgo');
};
concat: { main: {
src: [ 'js/utils.js', 'js/main.js' ],
dest: 'build/scripts.js' }},
watch: { concat: {
files: '<%= concat.main.src %>',
tasks: 'concat' }}
Упрощает инициализацию проектов и отдельных файлов (scaffolding).
$ grunt init:node
...
$ tree
.
├── LICENSE-MIT
├── README.md
├── grunt.js
├── lib
│ └── grunt-init-node-sample.js
├── package.json
└── test
└── grunt-init-node-sample_test.js
...
<script type="text/javascript" src="/Content/js/lib/modernizr.js"></script>
<script type="text/javascript" src="/Content/js/lib/jquery.js"></script>
<script type="text/javascript" src="/Content/js/lib/json.js"></script>
<script type="text/javascript" src="/Content/js/lib/jquery.selectBox.js"></script>
<script type="text/javascript" src="/Content/js/lib/jquery.fader.js"></script>
<script type="text/javascript" src="/Content/js/lib/jquery.jscrollpane.js"></script>
<script type="text/javascript" src="/Content/js/util.js"></script>
<script type="text/javascript" src="/Content/js/articul/tools.js"></script>
<script type="text/javascript" src="/Content/js/articul/nav-primary.js"></script>
<script type="text/javascript" src="/Content/js/articul/gallerysis.js"></script>
<script type="text/javascript" src="/Content/js/storejs/store_json2.min.js"></script>
<script type="text/javascript" src="/Content/js/swfobject/swfobject.js"></script>
<script type="text/javascript" src="/Content/js/form.js"></script>
<script type="text/javascript" src="/Content/js/articul/widgets/widgets.js"></script>
<script type="text/javascript" src="/Content/js/articul/accessibility.js"></script>
<script type="text/javascript" src="/Content/js/adriver/adriver.core.2.js"></script>
<script type="text/javascript" src='/Content/js/accessibility_sensitive.js'></script>
<script type="text/javascript" src="/Content/js/articul/announce.js" ></script>
<script type="text/javascript" src="/Content/js/lib/countdown.js" ></script>
<script type="text/javascript" src="/bitrix/components2/Articul.Sochi.Components/VideoBlockHomepageComponent/templates/.default/videoblock.js" ></script>
<script type="text/javascript" src="/Content/js/util.js" ></script>
<script type="text/javascript" src="/bitrix/components2/Articul.Sochi.Components/QuoteBlockHomepageComponent/templates/.default/script.js" ></script>
<script type="text/javascript" src="/bitrix/components2/Articul.Sochi.Objects/MapHomepageComponent/templates/.default/script.js" ></script>
<script type="text/javascript" src="/Content/js/lib/hammer.js" ></script>
<script type="text/javascript" src="/Content/js/lib/jquery.specialevent.hammer.js" ></script>
<script type="text/javascript" src="/Content/js/articul/event-scroller.js" ></script>
<script type="text/javascript" src="/Content/js/lib/jquery.jscrollpane.js" ></script>
<script type="text/javascript" src="/Content/js/lib/jquery.mousewheel.js" ></script>
<script type="text/javascript" src="/bitrix/components2/Articul.Sochi.Twitter/TweetListComponent/templates/.default/script.js" ></script>
<script type="text/javascript" src="/Content/js/articul/blogs.js" ></script>
<script type="text/javascript" src="/Content/js/image-centered.js" ></script>
<script type="text/javascript" src="/Content/js/articul/storage.js" ></script>
<script type="text/javascript" src="/Content/js/articul/polls.js" ></script>
<script type="text/javascript" src="/bitrix/components2/Articul.Sochi.Components/InterestingBlockHomepageComponent/templates/.default/script.js" ></script>
...
Главная страница реального проекта.
concat: { main: {
src: [
'Content/js/lib/modernizr.js',
'Content/js/lib/jquery.js',
'Content/js/articul/**/*.js', ...
],
dest: 'build/scripts.js'
} }
min: {
main: {
src: [ '<%= concat.main.dest %>' ],
dest: 'build/scripts.min.js'
}
}
concat: { main: {
src: [
'Content/css/main.css',
'Content/css/articul/pit.css', ...
],
dest: 'build/styles.css'
} }
...
<link rel="stylesheet" href="/build/styles.css">
<script src="/build/scripts.js"></script>
...
Презентация: sapegin.ru/pres/grunt
Примеры: github.com/sapegin/grunt-talk-examples