Gulp 使用 gulp-rev 处理一组文件

Gulp handle an array of files with gulp-rev

上下文

我有一个gulpfile.js。它基本上将两个带有 js 文件的文件夹连接到两个文件以导出到 dist 文件夹。它还添加了缓存破坏器和用于生产的丑化版本。

//gulpfile.js
const gulp = require('gulp');
const sass = require('gulp-sass');
const concat = require('gulp-concat');
const rename = require('gulp-rename');
const maps = require('gulp-sourcemaps');
const uglify = require('gulp-uglify');
const gulpif = require('gulp-if');
const inlineCss = require('gulp-inline-css');
const jsValidate = require('gulp-jsvalidate');
const notify = require('gulp-notify');
const rev = require('gulp-rev');
const fs = require("fs");
const del = require('del');

const distFolder = './myapp/dist/';

/* Concat all scripts and add cache buster */
gulp.task('app_concatScripts', (done) => {
    /*first remove old app.js files */
    return del([distFolder + "app-*.{js,map}"])
        .then(paths => {
            /*Concat files with sourcemaps, hash them and write to manifest*/
            return gulp.src("./myapp/js/source-app/*.js")
                .pipe(maps.init())
                .pipe(concat({
                    path: "app.js",
                    cwd: '',
                    newLine: '; \n'
                }))
                .pipe(gulp.dest(distFolder))
                .pipe(rev())
                .pipe(maps.write('./'))
                .pipe(gulp.dest(distFolder))
                .pipe(rev.manifest(distFolder + 'rev-manifest.json', {
                    base: distFolder,
                    merge: true
                }))
                .pipe(gulp.dest(distFolder));
        });

});

/* Minify scripts and add cache buster */
gulp.task('app_minifyScripts', (done) => {
    return gulp.src(distFolder + "app.js", {
            allowEmpty: true
        })
        .pipe(uglify())
        .on('error', notify.onError((error) => {
            console.log(error);
            return error.message + ' in ' + error.fileName;
        }))
        .pipe(rename("app.min.js"))
        .pipe(rev())
        .pipe(gulp.dest(distFolder))
        .pipe(rev.manifest(distFolder + 'rev-manifest.json', {
            base: distFolder,
            merge: true
        }))
        .pipe(gulp.dest(distFolder));
});

/* Concat all scripts and add cache buster, this function is exactly the same as app_concatScripts */
gulp.task('main_concatScripts', (done) => {
     /*Concat files with sourcemaps, hash them and write to manifest*/
    return del(["./myapp/dist/main-*.{js,map}"])
        .then(paths => {
            return gulp.src("./myapp/js/source-main/*.js")
                .pipe(maps.init())
                .pipe(concat({
                    path: "main.js",
                    cwd: '',
                    newLine: '; \n'
                }))
                .pipe(gulp.dest(distFolder))
                .pipe(rev())
                .pipe(maps.write('./'))
                .pipe(gulp.dest(distFolder))
                .pipe(rev.manifest(distFolder + 'rev-manifest.json', {
                    base: distFolder,
                    merge: true
                }))
                .pipe(gulp.dest(distFolder));
        });
});

/* Minify scripts and add cache buster, this function is the same as app_minifyScripts */
gulp.task('main_minifyScripts', (done) => {

    return gulp.src("./myapp/dist/main.js")
        .pipe(uglify())
        .on('error', notify.onError((error) => {
            console.log(error);
            return error.message + ' in ' + error.fileName;
        }))
        .pipe(rename("main.min.js"))
        .pipe(rev())
        .pipe(gulp.dest(distFolder))
        .pipe(rev.manifest(distFolder + 'rev-manifest.json', {
            base: distFolder,
            merge: true
        }))
        .pipe(gulp.dest(distFolder));
});


/* watch task */
gulp.task('watch', (done) => {
    gulp.watch('./myapp/js/source-app/*.js', gulp.series('app_concatScripts', 'app_minifyScripts'));
    gulp.watch('./myapp/js/source-main/*.js', gulp.series('main_concatScripts', 'main_minifyScripts'));
    done();
});
gulp.task('default', gulp.series('main_concatScripts', 'main_minifyScripts', 'app_concatScripts', 'app_minifyScripts', 'watch'));

输出

gulpfile.js 有效。它构建了一个 rev-manifest.json 像这样:

{
  "app.js": "app-234318a58d.js",
  "app.min.js": "app-7788fee7f3.min.js",
  "main.js": "main-60788c863c.js",
  "main.min.js": "main-1e92517890.min.js",
  "style.css": "style-296f22c598.css",
  "admin.css": "admin-a3742ed2f6.css",
}

问题

为了让它工作,我为每个 js 文件复制了 _concatScripts_minifyScripts 任务:app.jsmain.js。使 gulp 文件变得不必要的大且难以维护。

我试过的

我尝试使用数组来定义脚本源文件和输出名称。这是行不通的,因为循环块中的代码不会等待前一个块准备就绪。所以 minify 任务有时会失败,因为原始 js 文件尚不存在,有时 rev-manifest.json 会被先前的代码块覆盖。所以它不再是真正的 gulp 系列了。执行此类操作的最佳做​​法是什么?

const scripts = [{
    name: 'app',
    source: './myapp/js/source-app/*.js'
}, {
    name: 'main',
    source: './myapp/js/source-main/*.js'
}];

/* Concat all scripts and add cache buster */
gulp.task('concatScripts', (done) => {
    for (var i = 0; i < scripts.length; i++) {
        var script = scripts[i];
        del([distFolder + script.name + "-*.{js,map}"])
            .then(paths => {
                /*Concat files with sourcemaps, hash them and write to manifest*/
                gulp.src(script.source)
                    .pipe(maps.init())
                    .pipe(concat({
                        path: script.name + ".js",
                        cwd: '',
                        newLine: '; \n'
                    }))
                    .pipe(gulp.dest(distFolder))
                    .pipe(rev())
                    .pipe(maps.write('./'))
                    .pipe(gulp.dest(distFolder))
                    .pipe(rev.manifest(distFolder + 'rev-manifest.json', {
                        base: distFolder,
                        merge: true
                    }))
                    .pipe(gulp.dest(distFolder));
            });
    }

});

/* Minify scripts and add cache buster */
gulp.task('minifyScripts', (done) => {
    for (var i = 0; i < scripts.length; i++) {
        var script = scripts[i];
        gulp.src(distFolder + script.name + ".js", {
                allowEmpty: true
            })
            .pipe(uglify())
            .on('error', notify.onError((error) => {
                console.log(error);
                return error.message + ' in ' + error.fileName;
            }))
            .pipe(rename(script.name + ".min.js"))
            .pipe(rev())
            .pipe(gulp.dest(distFolder))
            .pipe(rev.manifest(distFolder + 'rev-manifest.json', {
                base: distFolder,
                merge: true
            }))
            .pipe(gulp.dest(distFolder));
    }
});

/* watch task */
gulp.task('watch', (done) => {
    gulp.watch(['./myapp/js/source-app/*.js', './myapp/js/source-main/*.js'], gulp.series('concatScripts', 'minifyScripts'));
    done();
});

gulp.task('default', gulp.series('concatScripts', 'minifyScripts', 'watch'));

更新

我的 gulp 文件现在看起来像这样:

//gulpfile.js
const gulp = require('gulp');
const sass = require('gulp-sass');
const concat = require('gulp-concat');
const rename = require('gulp-rename');
const maps = require('gulp-sourcemaps');
const uglify = require('gulp-uglify');
const gulpif = require('gulp-if');
const inlineCss = require('gulp-inline-css');
const jsValidate = require('gulp-jsvalidate');
const notify = require('gulp-notify');
const rev = require('gulp-rev');
const fs = require("fs");
const del = require('del');
/* Concat all scripts and add cache buster */

const distFolder = './myapp/dist/';

function concatTask(opts) {
    const taskName = opts.name + '_concatScripts';
    gulp.task(taskName, (done) => {
        /*Concat files with sourcemaps, hash them and write to manifest*/
        return del(opts.del)
            .then(paths => {
                return gulp.src(opts.source)
                    .pipe(maps.init())
                    .pipe(concat({
                        'path': opts.name + '.js',
                        'cwd': '',
                        'newLine': '; \n'
                    }))
                    .pipe(gulp.dest(distFolder))
                    .pipe(rev())
                    .pipe(maps.write('./'))
                    .pipe(gulp.dest(distFolder))
                    .pipe(rev.manifest(distFolder + 'rev-manifest.json', {
                        'base': distFolder,
                        'merge': true
                    }))
                    .pipe(gulp.dest(distFolder));
            });
    });
    return taskName;
}

function minifyTask(opts) {
    const taskName = opts.name + '_minifyScripts';
    gulp.task(taskName, (done) => {
        return gulp.src(distFolder + opts.name + '.js', opts.sourceParams)
            .pipe(uglify())
            .on('error', notify.onError((error) => {
                console.log(error);
                return error.message + ' in ' + error.fileName;
            }))
            .pipe(rename(opts.name + '.min.js'))
            .pipe(rev())
            .pipe(gulp.dest(distFolder))
            .pipe(rev.manifest(distFolder + 'rev-manifest.json', {
                'base': distFolder,
                'merge': true
            }))
            .pipe(gulp.dest(distFolder));
    });
    return taskName;
}

const taskOptions = [{
    'name': 'main',
    'source': './myapp/js/source-main/*.js',
    'sourceParams': {
        'allowEmpty': false
    },
    'del': ['./myapp/dist/main-*.{js,map}']
}, {
    'name': 'app',
    'source': './myapp/js/source-app/*.js',
    'sourceParams': {
        'allowEmpty': false
    },
    'del': ['./myapp/dist/app-*.{js,map}']
},

 ];

const taskNames = taskOptions.map(opts => {
    return [concatTask(opts), minifyTask(opts)];
});
/* equivalent to defining tasks and assigning:
 *  var taskNames = [
 *    ['main_concatScripts', 'main_minifyScripts'],
 *    ['app_concatScripts', 'app_minifyScripts']
 *  ];
 */

gulp.task('watch', (done) => {
    taskOptions.forEach((opts, i) => {
        gulp.watch(opts.source, gulp.series.apply(gulp, taskNames[i]));
    });
    done();
});
/* equivalent to:
 *  gulp.task('watch', (done) => {
 *    gulp.watch('./myapp/js/source-main/*.js', gulp.series('main_concatScripts', 'main_minifyScripts'));
 *    gulp.watch('./myapp/js/source-app/*.js', gulp.series('app_concatScripts', 'app_minifyScripts'));
 *    done();
 *  });
 */

const allTaskNames = taskNames.reduce((arr, names) => arr.concat(names), []); // flatmap the task names into a single array
// console.log(allTaskNames.concat(['watch']));
/* equivalent to:
 *  let allTaskNames = ['main_concatScripts', 'main_minifyScripts', 'app_concatScripts', 'app_minifyScripts'];
 */

gulp.task('default', gulp.series.apply(gulp, allTaskNames.concat(['styles', 'watch'])));
/* equivalent to:
 *  gulp.task('default', gulp.series('main_concatScripts', 'main_minifyScripts', 'app_concatScripts', 'app_minifyScripts', 'watch'));
 */

动态任务创建者工作。不幸的是,缩小任务在连接任务完成之前就开始了。我收到以下错误,添加 allowEmpty 选项确实有效,但随后错误的串联文件被缩小:

[12:57:19] Using gulpfile /repo/myapp/gulpfile.js
[12:57:19] Starting 'default'...
[12:57:19] Starting 'main_concatScripts'...
[12:57:19] Finished 'main_concatScripts' after 47 ms
[12:57:19] Starting 'main_minifyScripts'...
[12:57:19] 'main_minifyScripts' errored after 11 ms
[12:57:19] Error: File not found with singular glob: /repo/myapp/dist/main.js (if this was purposeful, use `allowEmpty` option)

如果只是保留代码的问题DRY,您可以编写两个包含 WET 代码泛化的函数:

  • concatTask()
  • minifyTask()

这些函数中的每一个都应该定义一个 gulp 任务和 return 一个内部生成的任务名称。

function concatTask(opts) {
    const taskName = opts.name + '_concatScripts';
    gulp.task(taskName, (done) => {
        /*Concat files with sourcemaps, hash them and write to manifest*/
        return del(opts.del)
        .then(paths => {
            return gulp.src(opts.source)
            .pipe(maps.init())
            .pipe(concat({
                'path': opts.name + '.js',
                'cwd': '',
                'newLine': '; \n'
            }))
            .pipe(gulp.dest(distFolder))
            .pipe(rev())
            .pipe(maps.write('./'))
            .pipe(gulp.dest(distFolder))
            .pipe(rev.manifest(distFolder + 'rev-manifest.json', {
                'base': distFolder,
                'merge': true
            }))
            .pipe(gulp.dest(distFolder));
        });
    });
    return taskName;
}

function minifyTask(opts) {
    const taskName = opts.name + '_minifyScripts';
    gulp.task(taskName, (done) => {
        return gulp.src(opts.source, opts.sourceParams)
        .pipe(uglify())
        .on('error', notify.onError((error) => {
            console.log(error);
            return error.message + ' in ' + error.fileName;
        }))
        .pipe(rename(opts.name + '.min.js'))
        .pipe(rev())
        .pipe(gulp.dest(distFolder))
        .pipe(rev.manifest(distFolder + 'rev-manifest.json', {
            'base': distFolder,
            'merge': true
        }))
        .pipe(gulp.dest(distFolder));
    });
    return taskName;
}

任务定义、监视和系列执行可以程序化如下:

const taskOptions = [
    {
        'name': 'main',
        'source': './myapp/js/source-main/*.js',
        'sourceParams': {},
        'del': ['./myapp/dist/main-*.{js,map}']
    },
    {
        'name': 'app',
        'source': './myapp/js/source-app/*.js',
        'sourceParams': { 'allowEmpty': true },
        'del': ['./myapp/dist/app-*.{js,map}']
    }
];

const taskNames = taskOptions.map(opts => {
    return [concatTask(opts), minifyTask(opts)];
});
/* equivalent to defining tasks and assigning:
 *  var taskNames = [
 *    ['main_concatScripts', 'main_minifyScripts'],
 *    ['app_concatScripts', 'app_minifyScripts']
 *  ];
 */

gulp.task('watch', (done) => {
    taskOptions.forEach((opts, i) => {
        gulp.watch(opts.source, gulp.series.apply(gulp, taskNames[i]));
    });
    done();
});
/* equivalent to:
 *  gulp.task('watch', (done) => {
 *    gulp.watch('./myapp/js/source-main/*.js', gulp.series('main_concatScripts', 'main_minifyScripts'));
 *    gulp.watch('./myapp/js/source-app/*.js', gulp.series('app_concatScripts', 'app_minifyScripts'));
 *    done();
 *  });
 */

const allTaskNames = taskNames.reduce((arr, names) => arr.concat(names), []); // flatmap the task names into a single array
/* equivalent to:
 *  let allTaskNames = ['main_concatScripts', 'main_minifyScripts', 'app_concatScripts', 'app_minifyScripts'];
 */

gulp.task('default', gulp.series.apply(gulp, allTaskNames.concat('watch')));
/* equivalent to:
 *  gulp.task('default', gulp.series('main_concatScripts', 'main_minifyScripts', 'app_concatScripts', 'app_minifyScripts', 'watch'));
 */

除了错误,一切都将按照原始代码中的相同顺序发生。

你最终得到的东西并不是特别不言自明。了解 Function.prototype.apply() 肯定会有帮助。

就个人而言,我会在部署的代码中留下 "equivalent to:..." 注释,以提醒自己(并向他人解释)实际发生的事情。

所以感谢 Roamer 如果想添加另一个任务来删除 js 文件。所以一个任务可以等待另一个任务完成。这是 gulp 文件:

//gulpfile.js
const gulp = require('gulp');
const sass = require('gulp-sass');
const concat = require('gulp-concat');
const rename = require('gulp-rename');
const maps = require('gulp-sourcemaps');
const uglify = require('gulp-uglify');
const gulpif = require('gulp-if');
const inlineCss = require('gulp-inline-css');
const jsValidate = require('gulp-jsvalidate');
const notify = require('gulp-notify');
const rev = require('gulp-rev');
const fs = require("fs");
const del = require('del');
/* Concat all scripts and add cache buster */

const distFolder = './myapp/dist/';

function delTask(opts) {
    const taskName = opts.name + '_delScripts';
    gulp.task(taskName, (done) => {
        /*Delete old files*/
        return del(opts.del)

    });
    return taskName;
}

function concatTask(opts) {
    const taskName = opts.name + '_concatScripts';
    gulp.task(taskName, (done) => {
        /*Concat files with sourcemaps, hash them and write to manifest*/
        return gulp.src(opts.source)
            .pipe(maps.init())
            .pipe(concat({
                'path': opts.name + '.js',
                'cwd': '',
                'newLine': '; \n'
            }))
            .pipe(gulp.dest(distFolder))
            .pipe(rev())
            .pipe(maps.write('./'))
            .pipe(gulp.dest(distFolder))
            .pipe(rev.manifest(distFolder + 'rev-manifest.json', {
                'base': distFolder,
                'merge': true
            }))
            .pipe(gulp.dest(distFolder));
    });
    return taskName;
}

function minifyTask(opts) {
    const taskName = opts.name + '_minifyScripts';
    gulp.task(taskName, (done) => {
        return gulp.src(distFolder + opts.name + '.js', opts.sourceParams)
            .pipe(uglify())
            .on('error', notify.onError((error) => {
                console.log(error);
                return error.message + ' in ' + error.fileName;
            }))
            .pipe(rename(opts.name + '.min.js'))
            .pipe(rev())
            .pipe(gulp.dest(distFolder))
            .pipe(rev.manifest(distFolder + 'rev-manifest.json', {
                'base': distFolder,
                'merge': true
            }))
            .pipe(gulp.dest(distFolder));
    });
    return taskName;
}

const taskOptions = [{
    'name': 'main',
    'source': './myapp/js/source-main/*.js',
    'sourceParams': {
        'allowEmpty': false
    },
    'del': ['./myapp/dist/main-*.{js,map}']
}, {
    'name': 'app',
    'source': './myapp/js/source-app/*.js',
    'sourceParams': {
        'allowEmpty': false
    },
    'del': ['./myapp/dist/app-*.{js,map}']
}];

const taskNames = taskOptions.map(opts => {
    return [delTask(opts), concatTask(opts), minifyTask(opts)];
});
/* equivalent to defining tasks and assigning:
 *  var taskNames = [
 *    ['main_delScripts', 'main_concatScripts', 'main_minifyScripts'],
 *    ['app_delScripts', 'app_concatScripts', 'app_minifyScripts']
 *  ];
 */

gulp.task('watch', (done) => {
    taskOptions.forEach((opts, i) => {
        gulp.watch(opts.source, gulp.series.apply(gulp, taskNames[i]));
    });
    done();
});
/* equivalent to:
 *  gulp.task('watch', (done) => {
 *    gulp.watch('./myapp/js/source-main/*.js', gulp.series('main_delScripts', 'main_concatScripts', 'main_minifyScripts'));
 *    gulp.watch('./myapp/js/source-app/*.js', gulp.series('app_delScripts', 'app_concatScripts', 'app_minifyScripts'));
 *    done();
 *  });
 */

const allTaskNames = taskNames.reduce((arr, names) => arr.concat(names), []); // flatmap the task names into a single array
// console.log(allTaskNames.concat(['watch']));
/* equivalent to:
 *  let allTaskNames = ['main_delScripts', 'main_concatScripts', 'main_minifyScripts', 'app_delScripts', 'app_concatScripts', 'app_minifyScripts'];
 */

gulp.task('default', gulp.series.apply(gulp, allTaskNames.concat(['styles', 'watch'])));
/* equivalent to:
 *  gulp.task('default', gulp.series('main_delScripts', 'main_concatScripts', 'main_minifyScripts', 'app_delScripts', 'app_concatScripts', 'app_minifyScripts', 'watch'));
 */

并且输出:

[16:40:31] Using gulpfile gulpfile.js
[16:40:31] Starting 'default'...
[16:40:31] Starting 'main_delScripts'...
[16:40:31] Finished 'main_delScripts' after 29 ms
[16:40:31] Starting 'main_concatScripts'...
[16:40:31] Finished 'main_concatScripts' after 84 ms
[16:40:31] Starting 'main_minifyScripts'...
[16:40:34] Finished 'main_minifyScripts' after 2.45 s
[16:40:34] Starting 'app_delScripts'...
[16:40:34] Finished 'app_delScripts' after 941 μs
[16:40:34] Starting 'app_concatScripts'...
[16:40:34] Finished 'app_concatScripts' after 58 ms
[16:40:34] Starting 'app_minifyScripts'...
[16:40:37] Finished 'app_minifyScripts' after 3.12 s
[16:40:37] Starting 'styles'...
[16:40:37] Finished 'styles' after 215 ms
[16:40:37] Starting 'watch'...
[16:40:37] Finished 'watch' after 19 ms
[16:40:37] Finished 'default' after 6.04 s