├── README.md └── flycheck-flow.el /README.md: -------------------------------------------------------------------------------- 1 | flycheck-flow 2 | ============= 3 | 4 | [![MELPA](http://melpa.org/packages/flycheck-flow-badge.svg)](http://melpa.org/#/flycheck-flow) 5 | 6 | Flycheck interface to [Flow](http://flowtype.org/), a static type 7 | checker, designed to find type errors in JavaScript programs. 8 | -------------------------------------------------------------------------------- /flycheck-flow.el: -------------------------------------------------------------------------------- 1 | ;;; flycheck-flow.el --- Support Flow in flycheck 2 | 3 | ;; Copyright (C) 2015 Lorenzo Bolla 4 | ;; 5 | ;; Author: Lorenzo Bolla 6 | ;; Created: 16 Septermber 2015 7 | ;; Version: 1.1 8 | ;; Package-Requires: ((flycheck "0.18") (json "1.4")) 9 | 10 | ;;; Commentary: 11 | 12 | ;; This package adds support for flow to flycheck. It requires 13 | ;; flow>=0.20.0. 14 | 15 | ;; To use it, add to your init.el: 16 | 17 | ;; (require 'flycheck-flow) 18 | ;; (add-hook 'javascript-mode-hook 'flycheck-mode) 19 | 20 | ;; You want to use flow in conjunction with other JS checkers. 21 | ;; E.g. to use with gjslint, add this to your init.el 22 | ;; (flycheck-add-next-checker 'javascript-gjslint 'javascript-flow) 23 | 24 | ;; For coverage warnings add this to your init.el 25 | ;; (flycheck-add-next-checker 'javascript-flow 'javascript-flow-coverage) 26 | 27 | ;;; License: 28 | 29 | ;; This file is not part of GNU Emacs. 30 | ;; However, it is distributed under the same license. 31 | 32 | ;; GNU Emacs is free software; you can redistribute it and/or modify 33 | ;; it under the terms of the GNU General Public License as published by 34 | ;; the Free Software Foundation; either version 3, or (at your option) 35 | ;; any later version. 36 | 37 | ;; GNU Emacs is distributed in the hope that it will be useful, 38 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 39 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 40 | ;; GNU General Public License for more details. 41 | 42 | ;; You should have received a copy of the GNU General Public License 43 | ;; along with GNU Emacs; see the file COPYING. If not, write to the 44 | ;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 45 | ;; Boston, MA 02110-1301, USA. 46 | 47 | ;;; Code: 48 | (require 'flycheck) 49 | (require 'json) 50 | 51 | (flycheck-def-args-var flycheck-javascript-flow-args javascript-flow) 52 | (customize-set-variable 'flycheck-javascript-flow-args '()) 53 | 54 | (defun flycheck-flow--parse-json (output checker buffer) 55 | "Parse flycheck json OUTPUT generated by CHECKER on BUFFER." 56 | (let* ((json-object-type 'alist) 57 | (json-array-type 'list) 58 | (flow-json-output (json-read-from-string output)) 59 | (flow-errors-list (cdr (assq 'errors flow-json-output))) 60 | message-kind 61 | message-level 62 | message-code-reason 63 | message-filename 64 | message-line 65 | message-column 66 | message-descr 67 | errors) 68 | (dolist (error-message flow-errors-list) 69 | ;; The structure for each `error-message' in `flow-errors-list' is like this: 70 | ;; ((kind . `message-kind') 71 | ;; (level . `message-level') 72 | ;; (message ((descr . `message-code-reason') 73 | ;; (loc (source . `message-filename') 74 | ;; (start (line . `message-line') (column . `message-column')))) 75 | ;; ((descr . `message-descr')))) 76 | (let-alist error-message 77 | (setq message-kind .kind) 78 | (setq message-level (intern .level)) 79 | 80 | (let-alist (car .message) 81 | (setq message-code-reason .descr 82 | message-filename .loc.source 83 | message-line .loc.start.line 84 | message-descr .descr 85 | message-column .loc.start.column)) 86 | 87 | (let-alist (car (cdr .message)) 88 | (when (string= .type "Comment") 89 | (setq message-descr .descr)))) 90 | 91 | (when (string= message-kind "parse") 92 | (setq message-descr message-kind)) 93 | 94 | (push (flycheck-error-new-at 95 | message-line 96 | message-column 97 | message-level 98 | message-descr 99 | :id message-code-reason 100 | :checker checker 101 | :buffer buffer 102 | :filename message-filename) 103 | errors)) 104 | (nreverse errors))) 105 | 106 | (defun read-first-line () 107 | "Return first line of current buffer." 108 | (save-excursion 109 | (goto-char (point-min)) 110 | (let ((b (point)) 111 | (e (progn (end-of-line) (point)))) 112 | (buffer-substring-no-properties b e)))) 113 | 114 | (defun flycheck-flow-tag-present-p () 115 | "Return true if the '// @flow' or '/* @flow */' tag is present in 116 | the first line of current buffer." 117 | (string-match-p "^\\(//+[@a-zA-Z ]*@flow\\|/\\**[@a-zA-Z ]*@flow\\)" (read-first-line))) 118 | 119 | (defun flycheck-flow--predicate () 120 | "Shall we run the checker?" 121 | (and 122 | buffer-file-name 123 | (file-exists-p buffer-file-name) 124 | (locate-dominating-file buffer-file-name ".flowconfig") 125 | (flycheck-flow-tag-present-p))) 126 | 127 | (flycheck-define-checker javascript-flow 128 | "A JavaScript syntax and style checker using Flow. 129 | 130 | See URL `http://flowtype.org/'." 131 | :command ( 132 | "flow" 133 | "check-contents" 134 | (eval flycheck-javascript-flow-args) 135 | "--json" 136 | "--from" "emacs" 137 | "--color=never" 138 | source-original) 139 | :standard-input t 140 | :predicate flycheck-flow--predicate 141 | :error-parser flycheck-flow--parse-json 142 | ;; js3-mode doesn't support jsx 143 | :modes (js-mode js-jsx-mode js2-mode js2-jsx-mode js3-mode web-mode rjsx-mode)) 144 | 145 | (flycheck-define-checker javascript-flow-coverage 146 | "A coverage checker for Flow. 147 | 148 | See URL `http://flowtype.org/'." 149 | :command ( 150 | "flow" 151 | "coverage" 152 | (eval flycheck-javascript-flow-args) 153 | "--json" 154 | "--from" "emacs" 155 | "--path" source-original) 156 | :standard-input t 157 | :predicate flycheck-flow--predicate 158 | :error-parser 159 | (lambda (output checker buffer) 160 | (let* ((json-array-type 'list) 161 | (json-object-type 'alist) 162 | (locs (condition-case nil 163 | (let ((report (json-read-from-string output))) 164 | (alist-get 'uncovered_locs (alist-get 'expressions report))) 165 | (error nil)))) 166 | (mapcar (lambda (loc) 167 | (let ((start (alist-get 'start loc)) 168 | (end (alist-get 'end loc))) 169 | (flycheck-error-new 170 | :buffer buffer 171 | :checker 'javascript-flow-coverage 172 | :filename buffer-file-name 173 | :line (alist-get 'line start) 174 | :column (alist-get 'column start) 175 | :message (format "no-coverage-to (%s . %s)" 176 | (alist-get 'line end) 177 | (alist-get 'column end)) 178 | :level 'warning))) 179 | locs))) 180 | ;; js3-mode doesn't support jsx 181 | :modes (js-mode js-jsx-mode js2-mode js2-jsx-mode js3-mode rjsx-mode)) 182 | 183 | (add-to-list 'flycheck-checkers 'javascript-flow) 184 | (add-to-list 'flycheck-checkers 'javascript-flow-coverage t) 185 | 186 | ;; allows eslint checks such as unused variables in addition to javascript-flow checker 187 | (flycheck-add-next-checker 'javascript-flow '(t . javascript-eslint) 'append) 188 | 189 | (provide 'flycheck-flow) 190 | ;;; flycheck-flow.el ends here 191 | --------------------------------------------------------------------------------