Gulp: передача параметров в задачу из декларации часов
Проблема: я хочу поддерживать "коллекции" файлов. Это поможет с временем сборки и гибкостью. например, каждый раз, когда я редактирую файл app.js, я не хочу перекомпилировать все мои загрузочные файлы Twitter.
Я могу, конечно, добиться этого с помощью двух задач и 2-х часовых деклараций - проблема в том, что задачи идентичны, кроме массива файлов. В идеале я хотел бы передать их как параметры в объявлении часов. Есть ли способ сделать что-то вроде следующего psuedo-кода?:
var files = {
scripts: [
'www/assets/scripts/plugins/**/*.js',
'www/assets/scripts/main.js',
],
vendor: [
'vendor/jquery/dist/jquery.js',
'vendor/jqueryui/ui/jquery.ui.widget.js',
'vendor/holderjs/holder.js'
],
};
...
gulp.task('js', ['lint'], function (files, output) {
return gulp.src(files)
.pipe(debug())
.pipe(concat(output))
.pipe(uglify({outSourceMap: true}))
.pipe(gulp.dest(targetJSDir))
.pipe(notify('JS minified'))
.on('error', gutil.log)
});
...
gulp.watch('scripts/**/*.js', ['lint', 'js'], files.scripts, 'app.min.js');
gulp.watch('vendor/**/*.js', ['lint', 'js'], files.vendor, 'vendor.min.js');
Перевертывание по-другому: должно ли пространство имен объявлять, которое вызвало задачу? Таким образом, я мог проверить, какие часы вызвали задачу, и условно те вещи внутри самой задачи.
Ответы
Ответ 1
проблема в том, что задачи идентичны, кроме массива файлов.
Я верю lazypipe (см. его страницу gh) хорошо
подходит для ваших нужд. Это была интересная проблема. Я попытаюсь ответить на то, о чем я думаю, о чем вы спрашиваете (что удовлетворено lazypipe), а также о том, что, на мой взгляд, вы, вероятно, думаете, или в конечном итоге подумаете о том, что вы прошли параметризацию труб проблема.
Один аспект того, что мы хотим, состоит в том, что мы не хотим повторно запускать jshint на файлы, которые не изменились. Кроме того, мы хотим сохранить его DRY, и мы хотим забрать новые файлы в дополнение к измененным.
Это проверено и работает для меня:
var gulp = require('gulp');
var $ = require('gulp-load-plugins')();
var es = require('event-stream');
var lazypipe = require('lazypipe');
var gutil = require('gulp-util');
var path = require('path');
var files = {
scripts: ['src/**/*.js'],
vendor: ['vendor/**/*.js']
};
// sets up a lazy pipe that does jshint related stuff
function getJsMultiPipe(name) {
return lazypipe()
.pipe($.jshint)
.pipe($.jshint.reporter, 'jshint-stylish')
// if you don't want to fail on style errors remove/comment this out:
.pipe($.jshint.reporter, 'fail');
}
// sets up a lazy pipe that does concat and post-concat stuff
function getJsCombinedPipe(groupName, outfile) {
return lazypipe()
.pipe($.concat, outfile)
.pipe($.uglify, {outSourceMap: true})
.pipe(gulp.dest, 'build')
.pipe($.notify, {message: groupName + ' JS minified', onLast: true});
}
// sets up a pipe for the initial build task, combining the above two pipes
function getBuildPipe(groupName, outfile) {
return gulp.src(files[groupName])
.pipe(getJsMultiPipe(groupName)())
.pipe(getJsCombinedPipe(groupName, outfile)());
}
// sets up a watch pipe, such that only the changed file is jshinted,
// but all files are included in the concat steps
function setWatchPipe(groupName, outfile) {
return $.watch({
glob: files[groupName],
name: groupName,
emitOnGlob: false,
emit: 'one'
}, function(file, done) {
return file
.pipe($.debug({title: 'watch -- changed file'}))
.pipe(getJsMultiPipe(groupName)())
// switch context
.pipe(gulp.src(files[groupName]))
.pipe($.debug({title: 'watch -- entire group'}))
.pipe(getJsCombinedPipe(groupName, outfile)())
.pipe($.debug({title: 'watch -- concatted/source-mapped'}))
.pipe($.notify({message: 'JS minified', onLast: true}));
});
}
// task to do an initial full build
gulp.task('build', function() {
return es.merge(
getBuildPipe('scripts', 'app.min.js'),
getBuildPipe('vendor', 'vendor.min.js')
)
.pipe($.notify({message: 'JS minified', onLast: true}));
});
// task to do an initial full build and then set up watches for
// incremental change
gulp.task('watch', ['build'], function(done) {
setWatchPipe('scripts', 'app.min.js');
setWatchPipe('vendor', 'vendor.min.js');
done();
});
Мои зависимости выглядят так:
"devDependencies": {
"jshint-stylish": "^0.1.5",
"gulp-concat": "^2.2.0",
"gulp-uglify": "^0.2.1",
"gulp-debug": "^0.3.0",
"gulp-notify": "^1.2.5",
"gulp-jshint": "^1.5.3",
"gulp": "^3.6.0",
"gulp-load-plugins": "^0.5.0",
"lazypipe": "^0.2.1",
"event-stream": "^3.1.1",
"gulp-util": "^2.2.14",
"gulp-watch": "^0.5.3"
}
РЕДАКТИРОВАТЬ: я просто взглянул на это снова, и я заметил следующие строки:
// switch context
.pipe(gulp.src(files[groupName]))
Имейте в виду, что я верю, что API gulp.src изменился с тех пор, как я написал это, и что он в настоящее время не переключает контекст, когда вы передаете вещи в gulp.src, поэтому это место может потребовать изменения, Для более новых версий gulp я думаю, что произойдет то, что вы добавите в контекст вместо этого и предположительно потеряете небольшую эффективность.
Ответ 2
Вы можете написать функцию-оболочку для задач для захвата параметров и передать ее задаче. Например. (с помощью библиотеки lodash):
// We capture the options in this object. We use gulp.env as a base such that
// options from cli are also passed to the task.
var currentOpts = _.clone(gulp.env);
// Here we define a function that wraps a task such that it can receive
// an options object
function parameterized(taskFunc) {
return function() {
taskFunc.call(null, currentOpts);
}
}
// Here we create a function that can be used by gulp.watch to call
// a parameterized task. It can be passed an object of "task" : {options} pairs
// and it will return a task function that will capture these options
// before invoking the task.
function withArgs(tasks) {
return function() {
_.each(tasks, function (opts, task) {
currentOpts = _.extend(currentOpts, opts);
gulp.run(task);
currentOpts = _.clone(gulp.env);
});
}
}
var files = {
scripts : [ "src/**/*.js"],
vendor : ["vendor/**/*.js"
};
// We pass the task function to parameterized. This will create a wrapper
// function that will pass an options object to the actual task function
gulp.task("js", parameterized(function(opts) {
gulp.src(files[opts.target])
.pipe(concat(opts.output));
}));
gulp.task("watch", function() {
// The withArgs function creates a watch function that invokes
// tasks with an options argument
// In this case it will invoke the js task with the options object
// { target : "scripts", output : "scripts.min.js" }
gulp.watch(files.scripts, withArgs({
js : {
target : "scripts",
output : "scripts.min.js"
}
}));
gulp.watch(files.vendor, withArgs({
js : {
target : "vendor",
output : "vendor.min.js"
}
}));
});
Ответ 3
Я столкнулся с той же проблемой - как передать параметры в задачу gulp. Было бы странно, что эта функция не встроена (это такая общая задача для создания, например, двух версий пакета, параметризованная задача кажется очень сухим решением).
Я хотел сделать это как можно более простым, поэтому мое решение состояло в том, чтобы динамически создавать задачи для каждого возможного параметра. Он работает нормально, если у вас есть небольшое количество точно определенных значений. Он не будет работать для значений большого диапазона, таких как int или float.
Определение задачи обернуто в функцию, которая принимает желаемый параметр, и параметр добавляется к имени задачи (с удобством для "$" ).
Ваш код может выглядеть так:
function construct_js(myname, files, output) {
gulp.task('js$' + myname, ['lint'], function () {
return gulp.src(files)
.pipe(debug())
.pipe(concat(output))
.pipe(uglify({outSourceMap: true}))
.pipe(gulp.dest(targetJSDir))
.pipe(notify('JS minified'))
.on('error', gutil.log)
});
}
construct_js("app", files.scripts, 'app.min.js');
construct_js("vendor", files.vendor, 'vendor.min.js');
gulp.watch('scripts/**/*.js', ['lint', 'js$app']);
gulp.watch('vendor/**/*.js', ['lint', 'js$vendor']);
Или лучше, с небольшим изменением в определении данных, мы вызываем создание задачи в цикле (поэтому, если вы добавите новую "версию" в массив configs, она будет работать сразу:
var configs = [
{
name : "app",
output: 'app.min.js',
files: [ 'www/assets/scripts/plugins/**/*.js',
'www/assets/scripts/main.js',
]
},
{
name : "vendor",
output: 'vendor.min.js',
files: [ 'vendor/jquery/dist/jquery.js',
'vendor/jqueryui/ui/jquery.ui.widget.js',
'vendor/holderjs/holder.js'
]
}
];
function construct_js(taskConfig) {
gulp.task('js$' + taskConfig.name, ['lint'], function () {
return gulp.src(taskConfig.files)
.pipe(debug())
.pipe(concat(taskConfig.output))
.pipe(uglify({outSourceMap: true}))
.pipe(gulp.dest(targetJSDir))
.pipe(notify('JS minified'))
.on('error', gutil.log)
});
}
for (var i=0; i < configs.length; i++) {
construct_js(configs[i]);
}
Если мы используем знак подчеркивания для последнего "для":
_(configs).each(construct_js);
Я использовал этот подход в своем проекте с хорошими результатами.
Ответ 4
Я хотел бы предложить несколько альтернатив. Предположим, что у нас есть задача, называемая build
, которую мы хотели бы условно угадать при заданном параметре.
Два подхода используют два часовых механизма с задачей одиночной сборки.
Альтернатива №1:
Вы можете использовать gulp-exec для запуска задачи с параметрами.
var exec = require('child_process').exec;
gulp.task('build', function(){
// Parse args here and determine whether to uglify or not
})
gulp.task('buildWithoutUglify' function(){
exec('gulp build --withoutUglify')
})
gulp.task('watch', function(){
gulp.watch(someFilePath, ['buildWithoutUglify'])
})
Обратите внимание на, что этот подход немного медленный, так как он выполняет команду командной строки gulp.
Альтернатива № 2:
Задайте глобальную переменную:
var withUglify = false;
gulp.task('build', function(){
// Use something like ``gulp-if`` to conditionally uglify.
})
gulp.task('buildWithoutUglify' function(){
withUglify = true;
gulp.start('build');
})
gulp.task('watch', function(){
gulp.watch(someFilePath, ['buildWithoutUglify'])
})