Node.js

#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 , и принятие решения станет сложнее. Восстановить абсолютный путь к исходному файлу тоже сложно, а также логика будет отличаться в зависимости от конкретной задачи. Какие-либо рекомендуемые шаблоны, где это возможно, для запоминания исходного файла?