【发布时间】:2019-03-07 21:41:55
【问题描述】:
我有一个 gulp 任务,它遍历一个文件夹以查找子文件夹并根据每个文件夹的内容输出一个 JavaScript 文件。下面是一个更直观的示例。
- 源
- 资产
- 脚本
- 严重
- 加载CSS.init.js
- 旧版
- flexibility.init.js
- picturefill.init.js
- 现代
- connectivity.js
- layzr.init.js
- menu_list.js
- 滚动提示.init.js
- slideout.init.js
- swiper.init.js
- 服务人员
- service-worker.js
- 严重
- 脚本
- 资产
变成:
- 开发
- 资产
- 脚本
- critical.js
- legacy.js
- modern.js
- service-worker.js
- 脚本
- 资产
这是通过读取 src/assets/scripts 目录的内容,然后对每个文件夹(critical、legacy、modern、service-worker)运行循环并将每个文件夹的内容发送到与merge-stream 合并在一起的 Gulp 任务。
这一切都很好,除了一旦任务重新合并在一起,我想在编译成功时触发通知。如果我尝试将任何内容通过管道传输到合并的流,它就不起作用。它只是返回合并的流,并且永远不会继续。
如果我不承诺我的 PROCESS_SCRIPTS 函数并且不使用合并流(即只处理一个手动指定的文件夹),它工作正常,所以我不知道发生了什么。
这是我的全部任务:
module.exports = {
scripts(gulp, plugins, ran_tasks, on_error) {
// task-specific plugins
const ESLINT = require("gulp-eslint");
const WEBPACK = require("webpack-stream");
// process scripts
const PROCESS_SCRIPTS = (js_directory, destination_file_name = "modern.js", compare_file_name = "modern.js", source = [global.settings.paths.src + "/assets/scripts/*.js"]) => {
return new Promise((resolve, reject) => {
const WEBPACK_CONFIG = {
mode: "development",
};
// update webpack config for the current target destination and file name
WEBPACK_CONFIG.mode = plugins.argv.dist ? "production" : WEBPACK_CONFIG.mode;
WEBPACK_CONFIG.output = {
filename: destination_file_name
};
const TASK = gulp.src(source)
// prevent breaking on error
.pipe(plugins.plumber({errorHandler: on_error}))
// check if source is newer than destination
.pipe(plugins.newer(js_directory + "/" + compare_file_name))
// lint all scripts
.pipe(ESLINT())
// print lint errors
.pipe(ESLINT.format())
// run webpack
.pipe(WEBPACK(WEBPACK_CONFIG))
// generate a hash and add it to the file name
.pipe(plugins.hash({template: "<%= name %>.<%= hash %><%= ext %>"}))
// output scripts to compiled directory
.pipe(gulp.dest(js_directory))
// generate a hash manfiest
.pipe(plugins.hash.manifest(".hashmanifest-scripts", {
deleteOld: true,
sourceDir: js_directory
}))
// output hash manifest in root
.pipe(gulp.dest("."))
// reject after errors
.on("error", () => {
reject(TASK);
})
// return the task after completion
.on("end", () => {
resolve(TASK);
});
});
};
// scripts task, lints, concatenates, & compresses JS
return new Promise ((resolve) => {
// set JS directory
const JS_DIRECTORY = plugins.argv.dist ? global.settings.paths.dist + "/assets/scripts" : global.settings.paths.dev + "/assets/scripts";
// set the source directory
const SOURCE_DIRECTORY = global.settings.paths.src + "/assets/scripts";
// set up an empty merged stream
const MERGED_STREAMS = plugins.merge();
// get the script source folder list
const SCRIPT_FOLDERS = plugins.fs.readdirSync(SOURCE_DIRECTORY);
// get the script destination file list
const SCRIPT_FILES = plugins.fs.existsSync(JS_DIRECTORY) ? plugins.fs.readdirSync(JS_DIRECTORY) : false;
// process all the script folders
const PROCESS_SCRIPT_FOLDERS = () => {
return Promise.resolve().then(() => {
// shift to the next folder
const FOLDER_NAME = SCRIPT_FOLDERS.shift();
// find the existing destination script file name
const FILE_NAME = SCRIPT_FILES ? SCRIPT_FILES.find((name) => {
return name.match(new RegExp(FOLDER_NAME + ".[a-z0-9]{8}.js"));
}) : FOLDER_NAME + ".js";
// process all scripts, update the stream
return PROCESS_SCRIPTS(JS_DIRECTORY, FOLDER_NAME + ".js", FILE_NAME, SOURCE_DIRECTORY + "/" + FOLDER_NAME + "/**/*").then((processed) => {
MERGED_STREAMS.add(processed);
});
}).then(() => SCRIPT_FOLDERS.length > 0 ? PROCESS_SCRIPT_FOLDERS() : resolve());
};
PROCESS_SCRIPT_FOLDERS().then(() => {
// wrap up
return MERGED_STREAMS
// prevent breaking on error
.pipe(plugins.plumber({
errorHandler: on_error,
}))
// notify that task is complete, if not part of default or watch
.pipe(plugins.gulpif(gulp.seq.indexOf("scripts") > gulp.seq.indexOf("default"), plugins.notify({
title: "Success!",
message: "Scripts task complete!",
onLast: true,
})))
// push task to ran_tasks array
.on("data", () => {
if (ran_tasks.indexOf("scripts") < 0) {
ran_tasks.push("scripts");
}
})
// resolve the promise on end
.on("end", () => {
resolve();
});
});
});
}
};
在我的 GitHub 上也可以看到:https://github.com/JacobDB/new-site/blob/master/gulp-tasks/scripts.js
编辑:我已经尝试了一些东西,我会在这里详细说明。
-
console.log("hello world")不会在MERGED_STREAMS.on("data")、MERGED_STREAMS.on("error")或MERGED_STREAMS.on("end")之后触发。 - 将
const MERGED_STREAMS = plugins.merge();移动到模块级变量(即紧跟在const WEBPACK = require("webpack-stream")之后)不会改变结果。 - 执行#2 然后使用
MERGED_STREAMS.add(gulp.src(source) ...)而不是在promise 完成后添加流不会改变结果,除非离开.pipe(gulp.dist("."))时需要输出.hashmanifest,并且始终将任务标记为跑了。 - 禁用
webpack、hash或eslint的任何组合都不会产生影响。 - 将
PROCESS_SCRIPTS从返回承诺更改为返回流,然后将每个文件夹作为单独的变量处理,然后手动合并它们似乎可以正确触发运行的任务,但webpack只能运行一次,所以它只输出一个文件——critical.hash.js。 注意:我没有在禁用hash的情况下测试此方法,如果始终输出.hashmanifest,这可能会导致它被标记为正确运行。 - 将 linting 步骤和 webpack 步骤拆分为单独的任务 kind of 会导致该任务被正确标记为已运行,但前提是
lint任务不是承诺,这会导致unexpected end of stream控制台出现错误。
编辑 2:根据@Louis 的建议更新了我的任务的修订版本。
【问题讨论】:
-
没有什么特别不对劲的。最明显的检查是
MERGED_STREAMS实际上有.pipe()和.on()方法。 -
是的,这就是让我感到困惑的地方,看起来还不错。
typeof MERGED_STREAMS.on和typeof MERGED_STREAMS.pipe都返回function所以我很确定这很好。 -
而
PROCESS_SCRIPT_FOLDERS().then(...)回调实际上会触发? -
是的,如果我在它输出的那一行之后放一个
console.log()。 -
是的,我会在有时间的时候继续研究它,如果我弄明白了会回来报告的。感谢您的帮助!
标签: javascript node.js promise stream gulp