#node.js #typescript #gulp #nodejs-stream
#node.js #typescript #gulp #nodejs-stream
Вопрос:
В приведенном ниже коде 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)
)));
Что не так в первой задаче? Я пропустил какой-то асинхронный вызов?
Ответ №1:
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"))));
Эта задача поместит в out
подкаталог все файлы, базовое имя которых "a"
. Вы также увидите на консоли абсолютный путь к каждому файлу, который проходит через условие, заданное для gulpIf
.
Вы могли бы изменить, fileShouldBePreprocessedBySass
чтобы принять виниловый файл и просто проверить file.path
там, вместо того, чтобы сохранять его, а затем извлекать.
Комментарии:
1. Спасибо за ответ! Получил сообщение о «порядок не гарантирован» и согласен, что, возможно, мой код хрупкий. Однако мне действительно нужно знать абсолютный путь к исходному файлу внутри конвейера, потому что в реальном проекте логика сложнее, и легче принимать решения на основе абсолютного пути к исходному файлу. После передачи файла
.pipe(gulpPlugins.pug())
расширение имени файла изменится на.html
, и принятие решения станет сложнее. Восстановить абсолютный путь к исходному файлу тоже сложно, а также логика будет отличаться в зависимости от конкретной задачи. Какие-либо рекомендуемые шаблоны, где это возможно, для запоминания исходного файла?