Как заставить Flow правильно работать с Vue 2 (webpack)?

Я пытаюсь добавить Flow в веб-шаблон Vue 2. Для записи я использую только время выполнения (файлы следуют за форматом .vue/стандарт).

Моя первая попытка состояла в том, чтобы использовать поток через cli, который я понял, что он не будет работать, потому что он не знал, как обрабатывать файлы .vue.

Моя вторая попытка состояла в том, чтобы добавить загрузчик webpack (а именно flow-status-webpack-plugin) и запустить проверку потока как часть сборки (например, eslint работает, например). Это не сработало, поэтому я рассмотрел другие варианты.

Моя третья попытка состояла в том, чтобы использовать плагин babel, который был сначала довольно успешным. Я использовал babel-plugin-typecheck + babel-plugin-syntax-flow. В Webpack нет выхода, однако ошибка типа приведет к поломке приложения. Я в порядке с этим подходом; он отлично справится с CI и сломает сборку.

Вот как выглядел мой .babelrc:

{
  ...
  "plugins": [
    ...
    ["typecheck", {
      "disable": {
        "production": true
      }
    }],
    "syntax-flow",
    "transform-flow-strip-types"
  ],
  ...
}

В этот момент поток работает как ожидается для глобальных методов, но не работает внутри компонента Vue:

<template>...</template>

<script>
/* @flow */
const flowIt = (a: number): number => {
  return a * 10
}

flowIt(20)
flowIt('bah') // Uncaught TypeError: Value of argument "a" violates contract. Expected: number Got: string

export default {    
  mounted: function () {
    flowIt(20)
    flowIt('bah') // Sees nothing wrong here
  }
}
</script>

<style>...</style>

Кроме того, цель состоит в том, чтобы не изменять код приложения из-за потока. В идеале я бы просто использовал Vue как обычно:

<template>...</template>

<script>
/* @flow */
export default {  
  methods: {
    flowIt (a: number): number {
      return a * 10
    }
  },

  mounted: function () {
    this.flowIt(20)
    this.flowIt('bah') // Should throw a type error.
  }
}
</script>

<style>...</style>

Не уверен, что это связано с Vue, как с моим опытом с Flow (подсказка: не так). Я думаю, мне нужны некоторые файлы типов, которые позволяют Flow понять, как структурирован компонент Vue (тот же, что и для директив, я думаю).

Для тех, у кого больше опыта с ним, как вы получили Flow для правильной работы с Vue + webpack?

Ответ 1

Вы все еще можете использовать Flow для части JS компонента .vue, комментируя фрагменты <template>, <style> и <script>:

 /* @flow
 <style>
 ...style definitions here
 </style>
 <template>
 ...html...
 </template>
 */
 // <script>
 export default {  
   methods: {
      flowIt (a: number): number {
         return a * 10
      }
   },

   mounted: function () {
      this.flowIt(20)
      this.flowIt('bah') //Won't throw error, as flowIt is attached to
                         //this.
   }
}
// </script>

Компилятор vue по-прежнему будет распознавать разделы <template>, <style> and <script> даже при комментировании, но контролер потока будет игнорировать их и обрабатывать только соответствующий раздел javascript.

К сожалению, это не даст вам 100% -ный охват типа, поскольку Flow не сможет проверить функции и объекты, прикрепленные к this (самому компоненту Vue), однако вы все же можете воспользоваться проверкой типа потока вызывает внешние функции (например, действия Vuex и геттеры, другие импортированные javascript модули), и если у вас есть расширенная бизнес-логика в методах компонентов, вы можете получить некоторую безопасность типов при работе с параметрами метода.

Ответ 2

Использование eslint + flow

Это еще один подход к интеграции потока и vue. Между тем flow пришел к eslint. Поэтому мы можем получить ошибки потока прямо как ошибки lint. Это более чистый подход, но затем поток становится связанным с вашим процессом сборки (вы не можете запускать flow check самостоятельно, но вам нужно запустить весь конвейер сборки через webpack, чтобы получить ошибки). Все еще ждут эту проблему, чтобы решить, иметь полную поддержку потока в файлах .vue с 10 мая 2017 года.

В большинстве случаев это нормально, но некоторые могут по-прежнему нуждаться в гибкости (и скорости) при запуске flow check. Это также может зависеть от вашей настройки CI.

Здесь вы можете настроить поток и eslint:

  • Установить deps

    yarn add \
      babel-plugin-syntax-flow \
      babel-plugin-transform-class-properties \
      babel-plugin-transform-flow-strip-types \
      eslint \
      babel-eslint \
      eslint-plugin-html \
      eslint-plugin-flowtype-errors \
      eslint-plugin-vue \
      eslint-config-vue \
      flow-bin \
    -D
    
  • Настроить .babelrc

    {
      ...
      "plugins": [
        "babel-plugin-transform-class-properties",
        "babel-plugin-syntax-flow",
        "babel-plugin-transform-flow-strip-types"
      ]
    }
    
  • Настроить .eslintrc

    {
      "parser": "babel-eslint",
    
      "plugins": [
        "html",
        "flowtype-errors"
      ],
    
      "extends": [
        "vue"
      ],
    
      "rules": {
        "flowtype-errors/show-errors": 2
      }
    }
    
  • Создайте файл .flowconfig. Он может быть пустым, если вам нечего настраивать.

В этом случае не требуется никаких дополнительных обходных решений, вы можете просто использовать /* @flow */ в тегах script в любом из ваших файлов .vue. См. Исходное сообщение здесь.

Ответ 3

В дополнение к Nik ответить, стоит упомянуть, что объединение его стратегии комментариев с проверкой времени выполнения делает "пакет" более полным. Один из способов сделать это - использовать babel-plugin-tcomb. Это сделает проверку выполнения частью процесса webpack/build (on save) + flow check как часть CI script.

Для разработки tcomb проведет проверку времени выполнения и вызовет исключение (консоль). Он не выполняет статические проверки, поэтому следующие

<script>
/* @flow */
const flowIt = (a: number): number => {
  return '' // Sees nothing wrong here, should be a number
}

// Vue component
export default {    
  ...
}
</script>

не будет работать должным образом. Однако следующее:

<template>{{ foo('bar') }} <!-- Type error --></template>
<script>
/* @flow */
const flowIt = (a: number): number => {
  return '' // Type error
}

// Vue component
export default {    
  methods: {
    foo: (x) => { flowIt(x) // Type error }
  },

  mounted: () => {
    flowIt([]) // Type error
  }
}
</script>

Это не идеально, но он проверяет после каждого сохранения, и это уловит большинство ошибок типа. Стоит упомянуть: tcomb использует те же аннотации (использует поток внутри), поэтому он работает из коробки.

Ofc, это не достаточно хорошо и нечестно побеждает точку потока. Решением этого является выполнение flow check на CI, как уже упоминалось. Для этого требуется ряд изменений:

  • Обновить .flowconfig для загрузки файлов .vue:

    ...
    [options]
    module.file_ext=.vue
    module.file_ext=.js
    ...
    
  • Включите блок шаблона и стиля в комментарий, содержащий @flow pragma; закомментируйте теги script (этот подход был упомянут здесь):

    /* @flow
    <template>...</template>
    
    <style>...</style>
    */
    
    // <script>
    ...
    // </script>
    

    Это немного неудобно, но я не мог найти лучшего способа. В идеале Flow сможет обрабатывать теги <script> в HTML-документе, но сейчас только в списке желаний (см. Вопрос).

  • Отключить tcomb в процессе производства

    {
      ...
      "plugins": [
        ...
        "syntax-flow",
        "transform-flow-strip-types"
      ],
      "env": {
        "development": {
          "plugins": ["tcomb"]
        }
      }
    }
    

Ответ 4

Я думаю, что это было решено тем временем, и теперь вы можете использовать Flow с Vue-компонентами без хаков. Посмотрите эту довольно блестящую статью для деталей конфигурации: https://alligator.io/vuejs/components-flow/

Ответ 5

Я реализовал шаблон проекта для vue с flow. https://github.com/wemake-services/wemake-vue-template

Он поддерживает отдельные компоненты файлов, листинг, тесты с jest, построением и обработкой на стороне сервера. Вот как выглядят ваши компоненты:

<template>
  <div class="page">
    <counter></counter>
    <p>
      To get started, edit files in <code>{{ path }}</code> and save.
    </p>
  </div>
</template>

<script>
// @flow
import Counter from 'components/Counter'
export default {
  components: {
    Counter
  },
  data () {
    const path: string = './client'
    return {
      path
    }
  }
}
</script>

Но будьте осторожны, что до сих пор невозможно комментировать некоторые части, такие как:

  • vuex
  • Vue.component, Vue.mixin

И, вероятно, некоторые другие. Это происходит из-за этой проблемы: https://github.com/facebook/flow/issues/452