Node.js & Gulp:真实的管道序列可能与书面的“.pipe()”链不同?
Node.js & Gulp: Real piping sequence could differ from written '.pipe()'s chain?
在下面的代码中,fileShouldBePreprocessedBySass()
将在 console.log('intercepted!');
执行之前被调用。此外,在 fileShouldBePreprocessedBySass(targetFileAbsolutePath)
中,参数 targetFileAbsolutePath
将是 undefined
:
let currentSourceFileAbsolutePath;
return gulp.src(entryPointsSourceFilesPathsOrGlobs)
// "gulpPlugins.intercept" is "gulp-intercept"
.pipe(gulpPlugins.intercept( sourceVynilFile => {
console.log('intercepted!');
currentSourceFileAbsolutePath = sourceVynilFile.path;
console.log(currentSourceFileAbsolutePath); // OK here
return sourceVynilFile;
}))
// "gulpPlugins.if" is "gulp-if"
.pipe(gulpPlugins.if(
// currentSourceFileAbsolutePath is undefined !!!
fileShouldBePreprocessedBySass(currentSourceFileAbsolutePath),
gulpPlugins.sass()
));
// ...
fileShouldBePreprocessedBySass(targetFileAbsolutePath) {
console.warn('---');
console.warn(targetFileAbsolutePath); // undefined!
const targetFilenameExtension = Path.extname(targetFileAbsolutePath);
let targetFilenameExtensionIsSupportedBySassPreprocessor = false;
for (const filenameExtension of SUPPORTED_FILENAME_EXTENSIONS__SASS_PREPROCESSOR) {
if (filenameExtension === targetFilenameExtension) {
targetFilenameExtensionIsSupportedBySassPreprocessor = true;
break;
}
}
return targetFilenameExtensionIsSupportedBySassPreprocessor;
}
真的,原来的代码是用TypeScript写的,但是我改写成JavaScript让更多人看懂代码。我之所以这么说是因为 TypeScript 编译器以某种方式理解了 pipe(gulpPlugins.if(/*...*/))
中的内容,参数 currentSourceFileAbsolutePath
未初始化,因此发生了 TS2454: Variable 'currentSourceFileAbsolutePath' is used before being assigned.
错误。
我很困惑,因为我有类似的任务可以正常工作(按正确的顺序):
let currentSourceFileAbsolutePath: string;
return gulp.src(entryPointsSourceFilesPathsOrGlobs)
.pipe(gulpPlugins.intercept(sourceVynilFile => {
currentSourceFileAbsolutePath = sourceVynilFile.path;
return sourceFile;
}))
.pipe(gulpPlugins.pug())
.pipe(gulpPlugins.intercept(compiledHtmlFile => {
// currentSourceFileAbsolutePath is NOT undefined!
if (shouldValidateCompiledHtmlRespectiveToSourceFile(currentSourceFileAbsolutePath)) {
HtmlValidator.validateHtml(compiledHtmlFile);
}
return compiledHtmlFile;
}))
.pipe(gulp.dest(() => (
// currentSourceFileAbsolutePath is NOT undefined!
getOutputDirectoryForPreprocessedMarkupEntryPointFileByRespectiveSourceFile(currentSourceFileAbsolutePath)
)));
第一个任务有什么问题?我错过了一些异步调用?
Gulp 本身不会对后续调用 .pipe
的内容重新排序。我不确定你的情况到底发生了什么,但你的代码 不必要地脆弱 。
考虑这个模式:
gulp.src("...")
.pipe(pluginA)
.pipe(pluginB)
并假设 pluginA
只是打印到控制台 pluginA
并且 pluginB
只是打印到控制台 pluginB
。您可能认为输出将是:
pluginA
pluginB
pluginA
pluginB
pluginA
...
也就是说,每个文件都会经过pluginA
,然后这个文件会经过pluginB
,然后下一个文件会经过pluginA
,等等。也就是说, pluginA
在 pluginB
处理完 pluginA
已完成处理的前一个文件之前,不会看到新文件。虽然在某些情况下此顺序正是将要发生的顺序,但对于一般情况,此顺序无法保证。你也可以拥有:
pluginA
pluginA
pluginA
pluginB
pluginA
pluginB
pluginB
pluginB
pluginA
...
文件必须首先被 pluginA
看到,但不能保证 pluginB
会在 pluginA
到达下一个文件之前处理文件。
如果处理顺序碰巧像我上面给出的第一个例子那样完美交错,您的代码就可以正常工作。但一般来说,这个顺序是不能保证的,你的代码会失败。
碰巧 您的代码的脆弱部分是不必要的。没有必要将文件的路径记录在文件流外部的变量中,然后在流的后面部分读取它。 gulp-if
会将它测试的文件传递给你给它的条件。这是一个例子:
const gulp = require("gulp");
const gulpIf = require("gulp-if");
gulp.task("default", () =>
gulp.src("src/**")
.pipe(gulpIf((file) => {
console.log(file.path);
return file.basename === "a";
}, gulp.dest("out"))));
此任务会将基本名称为 "a"
的所有文件放入 out
子目录中。您还将在控制台上看到每个文件的绝对路径,这些文件通过给定 gulpIf
的条件。
您可以修改 fileShouldBePreprocessedBySass
以接受 Vinyl 文件,然后只检查 file.path
那里而不是保存它然后检索它。
在下面的代码中,fileShouldBePreprocessedBySass()
将在 console.log('intercepted!');
执行之前被调用。此外,在 fileShouldBePreprocessedBySass(targetFileAbsolutePath)
中,参数 targetFileAbsolutePath
将是 undefined
:
let currentSourceFileAbsolutePath;
return gulp.src(entryPointsSourceFilesPathsOrGlobs)
// "gulpPlugins.intercept" is "gulp-intercept"
.pipe(gulpPlugins.intercept( sourceVynilFile => {
console.log('intercepted!');
currentSourceFileAbsolutePath = sourceVynilFile.path;
console.log(currentSourceFileAbsolutePath); // OK here
return sourceVynilFile;
}))
// "gulpPlugins.if" is "gulp-if"
.pipe(gulpPlugins.if(
// currentSourceFileAbsolutePath is undefined !!!
fileShouldBePreprocessedBySass(currentSourceFileAbsolutePath),
gulpPlugins.sass()
));
// ...
fileShouldBePreprocessedBySass(targetFileAbsolutePath) {
console.warn('---');
console.warn(targetFileAbsolutePath); // undefined!
const targetFilenameExtension = Path.extname(targetFileAbsolutePath);
let targetFilenameExtensionIsSupportedBySassPreprocessor = false;
for (const filenameExtension of SUPPORTED_FILENAME_EXTENSIONS__SASS_PREPROCESSOR) {
if (filenameExtension === targetFilenameExtension) {
targetFilenameExtensionIsSupportedBySassPreprocessor = true;
break;
}
}
return targetFilenameExtensionIsSupportedBySassPreprocessor;
}
真的,原来的代码是用TypeScript写的,但是我改写成JavaScript让更多人看懂代码。我之所以这么说是因为 TypeScript 编译器以某种方式理解了 pipe(gulpPlugins.if(/*...*/))
中的内容,参数 currentSourceFileAbsolutePath
未初始化,因此发生了 TS2454: Variable 'currentSourceFileAbsolutePath' is used before being assigned.
错误。
我很困惑,因为我有类似的任务可以正常工作(按正确的顺序):
let currentSourceFileAbsolutePath: string;
return gulp.src(entryPointsSourceFilesPathsOrGlobs)
.pipe(gulpPlugins.intercept(sourceVynilFile => {
currentSourceFileAbsolutePath = sourceVynilFile.path;
return sourceFile;
}))
.pipe(gulpPlugins.pug())
.pipe(gulpPlugins.intercept(compiledHtmlFile => {
// currentSourceFileAbsolutePath is NOT undefined!
if (shouldValidateCompiledHtmlRespectiveToSourceFile(currentSourceFileAbsolutePath)) {
HtmlValidator.validateHtml(compiledHtmlFile);
}
return compiledHtmlFile;
}))
.pipe(gulp.dest(() => (
// currentSourceFileAbsolutePath is NOT undefined!
getOutputDirectoryForPreprocessedMarkupEntryPointFileByRespectiveSourceFile(currentSourceFileAbsolutePath)
)));
第一个任务有什么问题?我错过了一些异步调用?
Gulp 本身不会对后续调用 .pipe
的内容重新排序。我不确定你的情况到底发生了什么,但你的代码 不必要地脆弱 。
考虑这个模式:
gulp.src("...")
.pipe(pluginA)
.pipe(pluginB)
并假设 pluginA
只是打印到控制台 pluginA
并且 pluginB
只是打印到控制台 pluginB
。您可能认为输出将是:
pluginA
pluginB
pluginA
pluginB
pluginA
...
也就是说,每个文件都会经过pluginA
,然后这个文件会经过pluginB
,然后下一个文件会经过pluginA
,等等。也就是说, pluginA
在 pluginB
处理完 pluginA
已完成处理的前一个文件之前,不会看到新文件。虽然在某些情况下此顺序正是将要发生的顺序,但对于一般情况,此顺序无法保证。你也可以拥有:
pluginA
pluginA
pluginA
pluginB
pluginA
pluginB
pluginB
pluginB
pluginA
...
文件必须首先被 pluginA
看到,但不能保证 pluginB
会在 pluginA
到达下一个文件之前处理文件。
如果处理顺序碰巧像我上面给出的第一个例子那样完美交错,您的代码就可以正常工作。但一般来说,这个顺序是不能保证的,你的代码会失败。
碰巧 您的代码的脆弱部分是不必要的。没有必要将文件的路径记录在文件流外部的变量中,然后在流的后面部分读取它。 gulp-if
会将它测试的文件传递给你给它的条件。这是一个例子:
const gulp = require("gulp");
const gulpIf = require("gulp-if");
gulp.task("default", () =>
gulp.src("src/**")
.pipe(gulpIf((file) => {
console.log(file.path);
return file.basename === "a";
}, gulp.dest("out"))));
此任务会将基本名称为 "a"
的所有文件放入 out
子目录中。您还将在控制台上看到每个文件的绝对路径,这些文件通过给定 gulpIf
的条件。
您可以修改 fileShouldBePreprocessedBySass
以接受 Vinyl 文件,然后只检查 file.path
那里而不是保存它然后检索它。