Предисловие
18 февраля вышел релиз Grunt v0.4.0, с чем всех и поздравляю. Если вы еще не знакомы с Грантом — прошу пройти на официальный сайт или почитать ознакомительную статью на Хабре. Вкратце, Грант позволяет автоматизировать склеивание и минификацию js-файлов, запуск тестов, проверку кода с помощью JSHint и многое другое.
Данная статья — история миграции одного приложения с Гранта v0.3.9 на вышедшую v0.4.0. Версии несовместимы и переезд оказался не таким простым делом, как я изначально предполагал. Полная инструкция по миграции на английском находится здесь, она подробнее чем мое описание.
Зачем я использую Grunt
Как любому ленивому frontend-разработчику, мне нужен был инструмент, который автоматизирует рутинные задачи, позволяя сосредоточиться непосредственно на разработке. Так я нашел Грант, который делал за меня следующее:
- компиляция stylus в css;
- склеивание js-файлов;
- проверка JavaScript линтером;
- минификация склеенных js-файлов;
- запуск unit-тестов (qUnit);
- отслеживание изменений исходных файлов и автоматический перезапуск вышеперечисленных задач.
Все эти важные, но скучные задачи выполнялись одной командой:
→ grunt
Running "stylus:compile" (stylus) task
File 'css/styles.css' created.
Running "concat:js" (concat) task
File "project.js" created.
Running "lint:files" (lint) task
Lint free.
Running "min:js" (min) task
File "project.min.js" created.
Uncompressed size: 130468 bytes.
Compressed size: 20937 bytes gzipped (74246 bytes minified).
Running "qunit:all" (qunit) task
Testing index.html...............OK
>> 95 assertions passed (594ms)
Running "watch" task
Waiting...
Все задачи описываются в специальном грант-файле: grunt.js. Для вышеприведенного лога он схематично выглядит так:
module.exports = function(grunt) {
grunt.initConfig({
stylus: {
// Компиляция Stylus в CSS
compile: {
options: {
'compress': true,
'paths': ['css/styl/']
},
files: {
'css/styles.css': 'css/styles.styl'
}
}
},
concat: {
// Склеивание js-файлов
js: {
src: [
/* Здесь большой список файлов */
],
dest: 'project.js'
}
},
min: {
// Минификация
js: {
src: ['<config:concat.js.dest>'],
dest: 'project.min.js'
}
},
jshint: {
options: {
smarttabs: true
}
},
lint: {
// Проверка кода
files: ['<config:concat.js.dest>']
},
watch: {
// Перекомпиляция стилей при изменении styl-файлов
stylus: {
files: ['css/styl/*.styl'],
tasks: 'stylus'
},
// Пересобирание скриптов и запуск lint при изменении исходных js-файлов
js: {
files: ['src/*.js'],
tasks: 'concat lint'
}
},
qunit: {
// Запуск написанных qUnit-тестов
all: ['../test/index.html']
}
});
// Загрузка модуля для компиляции Стилуса
grunt.loadNpmTasks('grunt-stylus');
// Объявление тасков
grunt.registerTask('default', 'stylus concat:js lint min:js qunit watch');
grunt.registerTask('test', 'qunit');
};
Не буду подробно его разбирать, комментариев в коде должно быть достаточно.
Миграция
Переустановка модуля
Ранее установленный глобально модуль grunt (если таковой имеется) удаляем: npm uninstall -g grunt
И устанавливаем модуль интерфейса командной строки Гранта: npm install -g grunt-cli
Сам grunt теперь ставится локально в папку проекта: npm install grunt
Проверяем версии модулей:
→ grunt --version
grunt-cli v0.1.6
grunt v0.4.0
Глобально разрешено поставить модуль grunt-init, но в моем приложении он не используется.
Перед установкой убедитесь, что версия node.js >= 0.8.0.
Переименование грант-файла
mv grunt.js Gruntfile.js
Грант-файл со старым именем больше не поддерживается, без переименования увидим ошибку:
Fatal error: Unable to find Gruntfile.
В новое версии грант-файл можно писать на CoffeeScript: Gruntfile.coffee.
Установка плагинов
У обновленного Гранта больше нет встроенных задач, таких как concat, min, watch и др. Их необходимо добавлять в виде отдельных плагинов:
- concat → grunt-contrib-concat
- lint → grunt-contrib-jshint
- min → grunt-contrib-uglify
- qunit → grunt-contrib-qunit
- watch → grunt-contrib-watch
Несложно заметить, что плагины Гранта имеют префикс grunt-contrib-.
Устанавливаем:
→ npm install grunt-contrib-concat
→ npm install grunt-contrib-jshint
→ npm install grunt-contrib-uglify
→ npm install grunt-contrib-qunit
→ npm install grunt-contrib-watch
→ npm install grunt-contrib-stylus
При установке рекомендуется использовать параметр --save-dev
, чтобы автоматически обновлялись зависимости devDependencies в package.json.
Подключаем плагины в грант-файле:
grunt.loadNpmTasks('grunt-contrib-concat');
grunt.loadNpmTasks('grunt-contrib-jshint');
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-contrib-qunit');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-contrib-stylus');
Изменения в грант-файле
Методу registerTask теперь нельзя передать список задач одной строкой с пробелами-разделителями. Строкой разрешено передать только одну задачу: grunt.registerTask('test', 'qunit');
Для списка задач обязательно используем массив:
grunt.registerTask('default', ['stylus', 'concat:js', 'jshint', 'min:js', 'qunit', 'watch']);
Вместо деректив вида <config:concat.js.dest>
теперь используем шаблоны: <%= concat.js.dest %>
.
Задача lint с опциями jshint теперь объединена в задачу jshint, где сразу можно указать опции:
jshint: {
options: {
smarttabs: true
},
js: ['project.js']
}
Задачу min переименовываем в uglify. Вместо объектов src/dest используем объект files:
uglify: {
js: {
files: {
'project.min.js': ['<%= concat.js.dest %>']
}
}
}
В задаче watch перечисляем выполняемые таски в виде массива:
watch: {
js: {
files: ['src/*.js'],
tasks: ['concat', 'lint']
}
}
На этом мой переезд был завершен, и Грант отработал без ошибок. Все изменения грант-файла собраны в этом шаблоне:
module.exports = function(grunt) {
grunt.initConfig({
// Компиляция Stylus в CSS
stylus: {
compile: {
options: {
'compress': true,
'paths': ['css/styl/']
},
files: {
'css/styles.css': 'css/styles.styl'
}
}
},
concat: {
// Склеивание js-файлов
js: {
src: [
/* Здесь большой список файлов */
],
dest: 'project.js'
}
},
uglify: {
// Минификация
js: {
files: {
'project.min.js': ['<%= concat.js.dest %>']
}
}
},
jshint: {
// Проверка кода
options: {
smarttabs: true
},
js: ['<%= concat.js.dest %>']
},
watch: {
// Перекомпиляция стилей при изменении styl-файлов
stylus: {
files: ['css/styl/*.styl'],
tasks: 'stylus'
},
// Пересобирание скриптов и запуск lint при изменении исходных js-файлов
js: {
files: ['src/*.js'],
tasks: ['concat', 'lint']
}
},
qunit: {
// Запуск написанных qUnit-тестов
all: ['../test/index.html']
}
});
grunt.loadNpmTasks('grunt-contrib-concat');
grunt.loadNpmTasks('grunt-contrib-jshint');
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-contrib-qunit');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-contrib-stylus');
// Объявление тасков
grunt.registerTask('default', ['stylus', 'concat:js', 'jshint', 'uglify:js', 'qunit', 'watch']);
grunt.registerTask('test', 'qunit');
};
Материалы по теме
Автор: bur