├── .babelrc ├── .flowconfig ├── .gitignore ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── bin └── flow-vim-quickfix ├── docs └── screenshot-1.png ├── flow-typed └── npm │ └── tape_v4.5.x.js ├── package.json ├── src ├── cli.js ├── formatQuickfix.js └── index.js └── test ├── fixture ├── 1.json └── 2.json └── formatQuickfix.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [ "transform-flow-strip-types" ], 3 | "presets": [ "es2015" ] 4 | } 5 | -------------------------------------------------------------------------------- /.flowconfig: -------------------------------------------------------------------------------- 1 | [ignore] 2 | 3 | [include] 4 | 5 | [libs] 6 | flow-typed/npm 7 | 8 | [options] 9 | suppress_comment= \\(.\\|\n\\)*\\$FlowExpectedError 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | lib/ 2 | node_modules/ 3 | .DS_Store 4 | npm-debug.log.* 5 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## How to File a Format Proposal 2 | 3 | Since this project aims for clear, easy-to-understand flow messages, your input is 4 | very valuable! 5 | 6 | Did you find any flow error messages which were confusing to you and you want it to be 7 | better? Please follow following steps: 8 | 9 | 1. Isolate your error (prevent noise of other flow errors) 10 | 2. Copy the output `flow --json` 11 | 3. Copy the output of `flow --json | flow-vim-quickfix` 12 | 4. Think about a format which would make more sense to you 13 | 5. Create an issue containing all the information acquired above 14 | 6. Profit. 15 | 16 | Thanks, 17 | 18 | Your feedback is very welcome :-) 19 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Patrick Stapfer 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # flow-vim-quickfix 2 | 3 | Commandline tool to either consume `flow --json` generated output via stdin or 4 | by spawning a specific `flow` executable on it's own and generate VIM quickfix 5 | friendly lines, ready for `errorformat` parsing. 6 | 7 | This tool was built for replacing the default flow maker in 8 | [Neomake](https://github.com/neomake/neomake) and thrives to enhance the UX for 9 | getting useful flow messages in VIM. 10 | 11 | ![](https://raw.github.com/ryyppy/flow-vim-quickfix/master/docs/screenshot-1.png) 12 | 13 | # Installation 14 | 15 | ``` 16 | $ npm install -g flow-vim-quickfix 17 | ``` 18 | 19 | ## Configuring NeoMake 20 | 21 | For now, you will need to add and enable a custom maker to your `.vimrc` or 22 | `.config/nvim/init.vim` file, like this: 23 | 24 | ```viml 25 | function! StrTrim(txt) 26 | return substitute(a:txt, '^\n*\s*\(.\{-}\)\n*\s*$', '\1', '') 27 | endfunction 28 | 29 | let g:neomake_javascript_enabled_makers = [] 30 | 31 | let g:flow_path = StrTrim(system('PATH=$(npm bin):$PATH && which flow')) 32 | 33 | if findfile('.flowconfig', '.;') !=# '' 34 | let g:flow_path = StrTrim(system('PATH=$(npm bin):$PATH && which flow')) 35 | if g:flow_path != 'flow not found' 36 | let g:neomake_javascript_flow_maker = { 37 | \ 'exe': 'sh', 38 | \ 'args': ['-c', g:flow_path.' --json 2> /dev/null | flow-vim-quickfix'], 39 | \ 'errorformat': '%E%f:%l:%c\,%n: %m', 40 | \ 'cwd': '%:p:h' 41 | \ } 42 | let g:neomake_javascript_enabled_makers = g:neomake_javascript_enabled_makers + [ 'flow'] 43 | endif 44 | endif 45 | 46 | " This is kinda useful to prevent Neomake from unnecessary runs 47 | if !empty(g:neomake_javascript_enabled_makers) 48 | autocmd! BufWritePost * Neomake 49 | endif 50 | ``` 51 | 52 | Now, after re-sourcing your vim config, the maker should run the flow binary and 53 | pipe it through `flow-vim-quickfix`. Why do we use `sh` here? Because the shell 54 | is much faster spawning the `flow` process than `node`, resulting in much faster 55 | feedback on each `BufWrite` in VIM. 56 | 57 | # Usage 58 | 59 | If you want to use the cli command for other purposes than VIM: 60 | 61 | ``` 62 | # Assuming you have been installing flow via `npm install -g flow-bin`... 63 | flow --json | flow-vim-quickfix 64 | 65 | # Or if you don't care about performance and want `node` to spawn flow for you 66 | flow-vim-quickfix /path/to/your/flow 67 | 68 | # You can also just use flow from $PATH 69 | flow-vim-quickfix flow 70 | 71 | # If you want to prevent stderr output like "Waiting for flow server..." 72 | flow --json 2> /dev/null | flow-vim-quickfix 73 | ``` 74 | -------------------------------------------------------------------------------- /bin/flow-vim-quickfix: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | require('../lib/cli.js'); 4 | -------------------------------------------------------------------------------- /docs/screenshot-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryyppy/flow-vim-quickfix/093a9d30c254fcd0929ad113b1639a6bcdf7339c/docs/screenshot-1.png -------------------------------------------------------------------------------- /flow-typed/npm/tape_v4.5.x.js: -------------------------------------------------------------------------------- 1 | // flow-typed signature: ca6833d62db13497465f89b8e2700ef7 2 | // flow-typed version: 7c31c25935/tape_v4.5.x/flow_>=v0.25.x 3 | 4 | /* @flow */ 5 | 6 | /* eslint-disable */ 7 | 8 | declare type tape$TestOpts = { 9 | skip: boolean, 10 | timeout?: number, 11 | } | { 12 | skip?: boolean, 13 | timeout: number, 14 | }; 15 | 16 | 17 | declare type tape$TestCb = (t: tape$Context) => void; 18 | declare type tape$TestFn = (a: string | tape$TestOpts | tape$TestCb, b?: tape$TestOpts | tape$TestCb, c?: tape$TestCb) => void; 19 | 20 | declare interface tape$Context { 21 | fail(msg?: string): void, 22 | pass(msg?: string): void, 23 | 24 | error(err: mixed, msg?: string): void, 25 | ifError(err: mixed, msg?: string): void, 26 | ifErr(err: mixed, msg?: string): void, 27 | iferror(err: mixed, msg?: string): void, 28 | 29 | ok(value: mixed, msg?: string): void, 30 | true(value: mixed, msg?: string): void, 31 | assert(value: mixed, msg?: string): void, 32 | 33 | notOk(value: mixed, msg?: string): void, 34 | false(value: mixed, msg?: string): void, 35 | notok(value: mixed, msg?: string): void, 36 | 37 | // equal + aliases 38 | equal(actual: mixed, expected: mixed, msg?: string): void, 39 | equals(actual: mixed, expected: mixed, msg?: string): void, 40 | isEqual(actual: mixed, expected: mixed, msg?: string): void, 41 | is(actual: mixed, expected: mixed, msg?: string): void, 42 | strictEqual(actual: mixed, expected: mixed, msg?: string): void, 43 | strictEquals(actual: mixed, expected: mixed, msg?: string): void, 44 | 45 | // notEqual + aliases 46 | notEqual(actual: mixed, expected: mixed, msg?: string): void, 47 | notEquals(actual: mixed, expected: mixed, msg?: string): void, 48 | notStrictEqual(actual: mixed, expected: mixed, msg?: string): void, 49 | notStrictEquals(actual: mixed, expected: mixed, msg?: string): void, 50 | isNotEqual(actual: mixed, expected: mixed, msg?: string): void, 51 | isNot(actual: mixed, expected: mixed, msg?: string): void, 52 | not(actual: mixed, expected: mixed, msg?: string): void, 53 | doesNotEqual(actual: mixed, expected: mixed, msg?: string): void, 54 | isInequal(actual: mixed, expected: mixed, msg?: string): void, 55 | 56 | // deepEqual + aliases 57 | deepEqual(actual: mixed, expected: mixed, msg?: string): void, 58 | deepEquals(actual: mixed, expected: mixed, msg?: string): void, 59 | isEquivalent(actual: mixed, expected: mixed, msg?: string): void, 60 | same(actual: mixed, expected: mixed, msg?: string): void, 61 | 62 | // notDeepEqual 63 | notDeepEqual(actual: mixed, expected: mixed, msg?: string): void, 64 | notEquivalent(actual: mixed, expected: mixed, msg?: string): void, 65 | notDeeply(actual: mixed, expected: mixed, msg?: string): void, 66 | notSame(actual: mixed, expected: mixed, msg?: string): void, 67 | isNotDeepEqual(actual: mixed, expected: mixed, msg?: string): void, 68 | isNotDeeply(actual: mixed, expected: mixed, msg?: string): void, 69 | isNotEquivalent(actual: mixed, expected: mixed, msg?: string): void, 70 | isInequivalent(actual: mixed, expected: mixed, msg?: string): void, 71 | 72 | // deepLooseEqual 73 | deepLooseEqual(actual: mixed, expected: mixed, msg?: string): void, 74 | looseEqual(actual: mixed, expected: mixed, msg?: string): void, 75 | looseEquals(actual: mixed, expected: mixed, msg?: string): void, 76 | 77 | // notDeepLooseEqual 78 | notDeepLooseEqual(actual: mixed, expected: mixed, msg?: string): void, 79 | notLooseEqual(actual: mixed, expected: mixed, msg?: string): void, 80 | notLooseEquals(actual: mixed, expected: mixed, msg?: string): void, 81 | 82 | throws(fn: Function, expected?: RegExp | Function, msg?: string): void, 83 | doesNotThrow(fn: Function, expected?: RegExp | Function, msg?: string): void, 84 | 85 | timeoutAfter(ms: number): void, 86 | 87 | skip(msg?: string): void, 88 | plan(n: number): void, 89 | onFinish(fn: Function): void, 90 | end(): void, 91 | comment(msg: string): void, 92 | test: tape$TestFn, 93 | } 94 | 95 | 96 | declare module 'tape' { 97 | declare type TestHarness = Tape; 98 | declare type StreamOpts = { 99 | objectMode?: boolean, 100 | }; 101 | 102 | declare type Tape = { 103 | (a: string | tape$TestOpts | tape$TestCb, b?: tape$TestCb | tape$TestOpts, c?: tape$TestCb, ...rest: Array): void, 104 | skip: (name: string, cb?: tape$TestCb) => void, 105 | createHarness: () => TestHarness, 106 | createStream: (opts?: StreamOpts) => stream$Readable, 107 | only: (a: string | tape$TestOpts | tape$TestCb, b?: tape$TestCb | tape$TestOpts, c?: tape$TestCb, ...rest: Array) => void, 108 | }; 109 | 110 | declare module.exports: Tape; 111 | } 112 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "flow-vim-quickfix", 3 | "version": "0.3.0", 4 | "description": "Node based tool to format `flow --json` output to a quickfix friendly format", 5 | "main": "./lib/index.js", 6 | "bin": "./bin/flow-vim-quickfix", 7 | "scripts": { 8 | "prepublish": "npm run build", 9 | "preversion": "npm test", 10 | "postversion": "git push && git push --tags", 11 | "test": "flow check; tape -r babel-register test/**/*.js", 12 | "build": "babel src --out-dir lib", 13 | "build:watch": "npm run build -- --watch" 14 | }, 15 | "files": [ 16 | "lib/", 17 | "bin/" 18 | ], 19 | "keywords": [], 20 | "author": "Patrick Stapfer ", 21 | "license": "MIT", 22 | "repository": "ryyppy/flow-vim-quickfix", 23 | "devDependencies": { 24 | "babel": "^6.5.2", 25 | "babel-cli": "^6.11.4", 26 | "babel-core": "^6.11.4", 27 | "babel-plugin-transform-flow-strip-types": "^6.8.0", 28 | "babel-preset-es2015": "^6.9.0", 29 | "flow-bin": "^0.30.0", 30 | "tape": "^4.6.0" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/cli.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | 4 | import formatQuickfix from './formatQuickfix'; 5 | import { exec } from 'child_process'; 6 | 7 | // TODO: refactor to child_process$Error 8 | // as soon as flow ships new node 9 | // definitions 10 | interface ProcessError extends Error { 11 | code: number; 12 | } 13 | 14 | type ReporterFn = (input: string) => string; 15 | 16 | function processFlowStdout(flowStdout: string): void { 17 | try { 18 | const flowJSON = JSON.parse(flowStdout); 19 | 20 | if (!(flowJSON instanceof Array) && flowJSON.passed === true) { 21 | process.exit(0); 22 | } 23 | 24 | const output = formatQuickfix(flowJSON); 25 | console.log(output.join('\n')); 26 | 27 | process.exit(1); 28 | } catch(e) { 29 | console.error(`Could not parse flow output json (${e.message})`); 30 | process.exit(3); 31 | } 32 | } 33 | 34 | const flow = process.argv[2]; 35 | 36 | // If there is a flow-binary defined, run a process 37 | if (flow != null) { 38 | exec(`${flow} check --json`, (err, stdout, stderr) => { 39 | // $FlowExpectedError 40 | if (err && err.code === 1) { 41 | console.error(`Error while calling flow: ${err.message}`); 42 | process.exit(3); 43 | } 44 | 45 | processFlowStdout(stdout.toString()); 46 | }); 47 | } 48 | else { 49 | // Otherwise read from stdin 50 | const { stdin } = process; 51 | 52 | stdin.setEncoding('utf8'); 53 | 54 | let data = ''; 55 | stdin.on('readable', () => { 56 | const chunk = process.stdin.read(); 57 | 58 | if (chunk != null) { 59 | data += chunk.toString(); 60 | } 61 | }); 62 | 63 | stdin.on('end', () => { 64 | processFlowStdout(data); 65 | }); 66 | } 67 | -------------------------------------------------------------------------------- /src/formatQuickfix.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | type FlowMessageBase = { 4 | context: ?string, 5 | descr: string, 6 | end: number, 7 | endline: number, 8 | line: number, 9 | path: string, 10 | start: number, 11 | } 12 | 13 | type FlowMessageBlame = FlowMessageBase & { 14 | type: 'Blame', 15 | loc: { 16 | source: string, 17 | type: 'SourceFile' | 'LibFile', 18 | start: { 19 | column: number, 20 | line: number, 21 | offset: number, 22 | }, 23 | end: { 24 | column: number, 25 | line: number, 26 | offset: number, 27 | }, 28 | }, 29 | }; 30 | 31 | type FlowMessageComment = FlowMessageBase & { type: 'Comment' }; 32 | 33 | type FlowMessage = FlowMessageBlame | FlowMessageComment; 34 | 35 | type FlowError = { 36 | kind: 'infer', 37 | level: 'error', 38 | message: Array, 39 | }; 40 | 41 | type FlowJsonOutput = { 42 | errors?: Array, 43 | flowVersion: string, 44 | passed: boolean, 45 | }; 46 | 47 | function getLocTypeTag(type: string): string { 48 | switch(type) { 49 | case 'LibFile': return '[LIB]'; 50 | // case 'SourceFile': return '[SO]'; 51 | default: return ''; 52 | } 53 | } 54 | 55 | // Separates the error messages in it's related sub-messages 56 | function groupMessages(message: Array): Array> { 57 | const { result } = message.reduce((ret, msg) => { 58 | 59 | if(msg.type === 'Blame') { 60 | if (ret.buffer.length > 0) { 61 | ret.result.push(ret.buffer); 62 | } 63 | 64 | ret.buffer = [msg]; 65 | } 66 | else { 67 | ret.buffer.push(msg); 68 | } 69 | 70 | return ret; 71 | }, { buffer: [], result: [] }); 72 | 73 | return result; 74 | } 75 | 76 | function processMessage(message: Array): string { 77 | return message.reduce((str, msg) => { 78 | if (msg.type === 'Blame') { 79 | // On start of the whole formatted message 80 | if (!str) { 81 | const locTypeTag = getLocTypeTag(msg.loc.type); 82 | const locTypeTagStr = (locTypeTag !== '' ? ` ${locTypeTag} |`: ''); 83 | 84 | return `${msg.path}:${msg.line}:${msg.start},${msg.end}:${locTypeTagStr} ${msg.descr}`; 85 | } 86 | else { 87 | if (str.indexOf('Property not found in') !== -1) { 88 | const context = (msg.context ? ` | ${msg.context.trim()}` : '') 89 | return `${str} \`${msg.descr}\`${context}`; 90 | } 91 | return `${str} ${msg.descr}`; 92 | } 93 | } 94 | 95 | if (msg.type === 'Comment') { 96 | return `${str} | ${msg.descr}`; 97 | } 98 | 99 | return str; 100 | }, ''); 101 | } 102 | 103 | export default function formatQuickfix(jsonOutput: FlowJsonOutput): Array { 104 | const { errors } = jsonOutput; 105 | 106 | if (errors == null) { 107 | return []; 108 | } 109 | 110 | return errors.map((err) => processMessage(err.message)); 111 | } 112 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | export { default as formatQuickfix } from './formatQuickfix'; 4 | -------------------------------------------------------------------------------- /test/fixture/1.json: -------------------------------------------------------------------------------- 1 | { 2 | "errors": [ 3 | { 4 | "kind": "infer", 5 | "level": "error", 6 | "message": [ 7 | { 8 | "context": " | $npm$ReduxSaga$PutEffect", 9 | "descr": "$npm$ReduxSaga$PutEffect", 10 | "end": 28, 11 | "endline": 25, 12 | "line": 25, 13 | "loc": { 14 | "end": { 15 | "column": 28, 16 | "line": 25, 17 | "offset": 550 18 | }, 19 | "source": "redux-saga_0.9.5.js", 20 | "start": { 21 | "column": 5, 22 | "line": 25, 23 | "offset": 526 24 | }, 25 | "type": "LibFile" 26 | }, 27 | "path": "redux-saga_0.9.5.js", 28 | "start": 5, 29 | "type": "Blame" 30 | }, 31 | { 32 | "context": null, 33 | "descr": "Application of polymorphic type needs . (Can use `*` for inferrable ones)", 34 | "end": 0, 35 | "endline": 0, 36 | "line": 0, 37 | "path": "", 38 | "start": 1, 39 | "type": "Comment" 40 | } 41 | ] 42 | }, 43 | { 44 | "kind": "infer", 45 | "level": "error", 46 | "message": [ 47 | { 48 | "context": " | $npm$ReduxSaga$CallEffect;", 49 | "descr": "$npm$ReduxSaga$CallEffect", 50 | "end": 29, 51 | "endline": 26, 52 | "line": 26, 53 | "loc": { 54 | "end": { 55 | "column": 29, 56 | "line": 26, 57 | "offset": 580 58 | }, 59 | "source": "redux-saga_0.9.5.js", 60 | "start": { 61 | "column": 5, 62 | "line": 26, 63 | "offset": 555 64 | }, 65 | "type": "LibFile" 66 | }, 67 | "path": "redux-saga_0.9.5.js", 68 | "start": 5, 69 | "type": "Blame" 70 | }, 71 | { 72 | "context": null, 73 | "descr": "Application of polymorphic type needs . (Can use `*` for inferrable ones)", 74 | "end": 0, 75 | "endline": 0, 76 | "line": 0, 77 | "path": "", 78 | "start": 1, 79 | "type": "Comment" 80 | } 81 | ] 82 | }, 83 | { 84 | "kind": "infer", 85 | "level": "error", 86 | "message": [ 87 | { 88 | "context": "import { createStore, applyMiddleware, compose } from 'redux';", 89 | "descr": "redux", 90 | "end": 61, 91 | "endline": 5, 92 | "line": 5, 93 | "loc": { 94 | "end": { 95 | "column": 61, 96 | "line": 5, 97 | "offset": 133 98 | }, 99 | "source": "test_redux-saga_0.9.5.js", 100 | "start": { 101 | "column": 55, 102 | "line": 5, 103 | "offset": 126 104 | }, 105 | "type": "SourceFile" 106 | }, 107 | "path": "test_redux-saga_0.9.5.js", 108 | "start": 55, 109 | "type": "Blame" 110 | }, 111 | { 112 | "context": null, 113 | "descr": "Required module not found", 114 | "end": 0, 115 | "endline": 0, 116 | "line": 0, 117 | "path": "", 118 | "start": 1, 119 | "type": "Comment" 120 | } 121 | ] 122 | }, 123 | { 124 | "kind": "infer", 125 | "level": "error", 126 | "message": [ 127 | { 128 | "context": "import createSagaMiddleware from 'redux-saga';", 129 | "descr": "redux-saga", 130 | "end": 45, 131 | "endline": 6, 132 | "line": 6, 133 | "loc": { 134 | "end": { 135 | "column": 45, 136 | "line": 6, 137 | "offset": 180 138 | }, 139 | "source": "test_redux-saga_0.9.5.js", 140 | "start": { 141 | "column": 34, 142 | "line": 6, 143 | "offset": 168 144 | }, 145 | "type": "SourceFile" 146 | }, 147 | "path": "test_redux-saga_0.9.5.js", 148 | "start": 34, 149 | "type": "Blame" 150 | }, 151 | { 152 | "context": null, 153 | "descr": "Required module not found", 154 | "end": 0, 155 | "endline": 0, 156 | "line": 0, 157 | "path": "", 158 | "start": 1, 159 | "type": "Comment" 160 | } 161 | ] 162 | }, 163 | { 164 | "kind": "infer", 165 | "level": "error", 166 | "message": [ 167 | { 168 | "context": "import { take, call, put, fork } from 'redux-saga/effects';", 169 | "descr": "redux-saga/effects", 170 | "end": 58, 171 | "endline": 7, 172 | "line": 7, 173 | "loc": { 174 | "end": { 175 | "column": 58, 176 | "line": 7, 177 | "offset": 240 178 | }, 179 | "source": "test_redux-saga_0.9.5.js", 180 | "start": { 181 | "column": 39, 182 | "line": 7, 183 | "offset": 220 184 | }, 185 | "type": "SourceFile" 186 | }, 187 | "path": "test_redux-saga_0.9.5.js", 188 | "start": 39, 189 | "type": "Blame" 190 | }, 191 | { 192 | "context": null, 193 | "descr": "Required module not found", 194 | "end": 0, 195 | "endline": 0, 196 | "line": 0, 197 | "path": "", 198 | "start": 1, 199 | "type": "Comment" 200 | } 201 | ] 202 | }, 203 | { 204 | "kind": "infer", 205 | "level": "error", 206 | "message": [ 207 | { 208 | "context": "type Effect = $npm$ReduxSaga$Effect;", 209 | "descr": "identifier `$npm$ReduxSaga$Effect`", 210 | "end": 35, 211 | "endline": 13, 212 | "line": 13, 213 | "loc": { 214 | "end": { 215 | "column": 35, 216 | "line": 13, 217 | "offset": 359 218 | }, 219 | "source": "test_redux-saga_0.9.5.js", 220 | "start": { 221 | "column": 15, 222 | "line": 13, 223 | "offset": 338 224 | }, 225 | "type": "SourceFile" 226 | }, 227 | "path": "test_redux-saga_0.9.5.js", 228 | "start": 15, 229 | "type": "Blame" 230 | }, 231 | { 232 | "context": null, 233 | "descr": "Could not resolve name", 234 | "end": 0, 235 | "endline": 0, 236 | "line": 0, 237 | "path": "", 238 | "start": 1, 239 | "type": "Comment" 240 | } 241 | ] 242 | }, 243 | { 244 | "kind": "infer", 245 | "level": "error", 246 | "message": [ 247 | { 248 | "context": " const { type } = action;", 249 | "descr": "property `type`", 250 | "end": 18, 251 | "endline": 26, 252 | "line": 26, 253 | "loc": { 254 | "end": { 255 | "column": 18, 256 | "line": 26, 257 | "offset": 747 258 | }, 259 | "source": "test_redux-saga_0.9.5.js", 260 | "start": { 261 | "column": 15, 262 | "line": 26, 263 | "offset": 743 264 | }, 265 | "type": "SourceFile" 266 | }, 267 | "path": "test_redux-saga_0.9.5.js", 268 | "start": 15, 269 | "type": "Blame" 270 | }, 271 | { 272 | "context": null, 273 | "descr": "Property not found in", 274 | "end": 0, 275 | "endline": 0, 276 | "line": 0, 277 | "path": "", 278 | "start": 1, 279 | "type": "Comment" 280 | }, 281 | { 282 | "context": " const { type } = action;", 283 | "descr": "Array", 284 | "end": 18, 285 | "endline": 26, 286 | "line": 26, 287 | "loc": { 288 | "end": { 289 | "column": 18, 290 | "line": 26, 291 | "offset": 747 292 | }, 293 | "source": "test_redux-saga_0.9.5.js", 294 | "start": { 295 | "column": 15, 296 | "line": 26, 297 | "offset": 743 298 | }, 299 | "type": "SourceFile" 300 | }, 301 | "path": "test_redux-saga_0.9.5.js", 302 | "start": 15, 303 | "type": "Blame" 304 | } 305 | ] 306 | } 307 | ], 308 | "flowVersion": "0.29.0", 309 | "passed": false 310 | } 311 | -------------------------------------------------------------------------------- /test/fixture/2.json: -------------------------------------------------------------------------------- 1 | { 2 | "errors": [ 3 | { 4 | "kind": "infer", 5 | "level": "error", 6 | "message": [ 7 | { 8 | "context": " | $npm$ReduxSaga$PutEffect", 9 | "descr": "$npm$ReduxSaga$PutEffect", 10 | "end": 28, 11 | "endline": 25, 12 | "line": 25, 13 | "loc": { 14 | "end": { 15 | "column": 28, 16 | "line": 25, 17 | "offset": 550 18 | }, 19 | "source": "/Projects/test/interface/redux-saga_0.9.5.js", 20 | "start": { 21 | "column": 5, 22 | "line": 25, 23 | "offset": 526 24 | }, 25 | "type": "LibFile" 26 | }, 27 | "path": "/Projects/test/interface/redux-saga_0.9.5.js", 28 | "start": 5, 29 | "type": "Blame" 30 | }, 31 | { 32 | "context": null, 33 | "descr": "Application of polymorphic type needs . (Can use `*` for inferrable ones)", 34 | "end": 0, 35 | "endline": 0, 36 | "line": 0, 37 | "path": "", 38 | "start": 1, 39 | "type": "Comment" 40 | } 41 | ] 42 | }, 43 | { 44 | "kind": "infer", 45 | "level": "error", 46 | "message": [ 47 | { 48 | "context": " | $npm$ReduxSaga$CallEffect;", 49 | "descr": "$npm$ReduxSaga$CallEffect", 50 | "end": 29, 51 | "endline": 26, 52 | "line": 26, 53 | "loc": { 54 | "end": { 55 | "column": 29, 56 | "line": 26, 57 | "offset": 580 58 | }, 59 | "source": "/Projects/test/interface/redux-saga_0.9.5.js", 60 | "start": { 61 | "column": 5, 62 | "line": 26, 63 | "offset": 555 64 | }, 65 | "type": "LibFile" 66 | }, 67 | "path": "/Projects/test/interface/redux-saga_0.9.5.js", 68 | "start": 5, 69 | "type": "Blame" 70 | }, 71 | { 72 | "context": null, 73 | "descr": "Application of polymorphic type needs . (Can use `*` for inferrable ones)", 74 | "end": 0, 75 | "endline": 0, 76 | "line": 0, 77 | "path": "", 78 | "start": 1, 79 | "type": "Comment" 80 | } 81 | ] 82 | }, 83 | { 84 | "kind": "infer", 85 | "level": "error", 86 | "message": [ 87 | { 88 | "context": " const { type } = action;", 89 | "descr": "property `type`", 90 | "end": 18, 91 | "endline": 26, 92 | "line": 26, 93 | "loc": { 94 | "end": { 95 | "column": 18, 96 | "line": 26, 97 | "offset": 747 98 | }, 99 | "source": "/Projects/test/test_redux-saga_0.9.5.js", 100 | "start": { 101 | "column": 15, 102 | "line": 26, 103 | "offset": 743 104 | }, 105 | "type": "SourceFile" 106 | }, 107 | "path": "/Projects/test/test_redux-saga_0.9.5.js", 108 | "start": 15, 109 | "type": "Blame" 110 | }, 111 | { 112 | "context": null, 113 | "descr": "Property not found in", 114 | "end": 0, 115 | "endline": 0, 116 | "line": 0, 117 | "path": "", 118 | "start": 1, 119 | "type": "Comment" 120 | }, 121 | { 122 | "context": " const { type } = action;", 123 | "descr": "Array", 124 | "end": 18, 125 | "endline": 26, 126 | "line": 26, 127 | "loc": { 128 | "end": { 129 | "column": 18, 130 | "line": 26, 131 | "offset": 747 132 | }, 133 | "source": "/Projects/test/test_redux-saga_0.9.5.js", 134 | "start": { 135 | "column": 15, 136 | "line": 26, 137 | "offset": 743 138 | }, 139 | "type": "SourceFile" 140 | }, 141 | "path": "/Projects/test/test_redux-saga_0.9.5.js", 142 | "start": 15, 143 | "type": "Blame" 144 | } 145 | ] 146 | } 147 | ], 148 | "flowVersion": "0.29.0", 149 | "passed": false 150 | } 151 | -------------------------------------------------------------------------------- /test/formatQuickfix.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import test from 'tape'; 4 | import formatQuickfix from '../src/formatQuickfix'; 5 | import F1 from './fixture/1.json'; 6 | import F2 from './fixture/2.json'; 7 | 8 | test('formatQuickfix', (t) => { 9 | t.test('with fixture/1.json', (q) => { 10 | const lines = formatQuickfix(F1) 11 | 12 | q.equals(lines.length, 7, 'there should be the appropriate number of lines'); 13 | 14 | q.equals(lines[0], 'redux-saga_0.9.5.js:25:5,28: [LIB] | $npm$ReduxSaga$PutEffect | Application of polymorphic type needs . (Can use `*` for inferrable ones)', 'line 1 should have the right format'); 15 | q.equals(lines[1], 'redux-saga_0.9.5.js:26:5,29: [LIB] | $npm$ReduxSaga$CallEffect | Application of polymorphic type needs . (Can use `*` for inferrable ones)', 'line 2 should have the right format'); 16 | // q.equals(lines[2], 'test_redux-saga_0.9.5.js:5:55,61: redux | Required module not found', 'line 3 should have the right format'); 17 | // q.equals(lines[3], 'test_redux-saga_0.9.5.js:6:34,45: redux-saga | Required module not found', 'line 4 should have the right format'); 18 | // q.equals(lines[4], 'test_redux-saga_0.9.5.js:7:39,58: redux-saga/effects | Required module not found', 'line 5 should have the right format'); 19 | // q.equals(lines[5], 'test_redux-saga_0.9.5.js:13:15,35: identifier `$npm$ReduxSaga$Effect` | Could not resolve name', 'line 6 should have the right format'); 20 | // q.equals(lines[6], 'test_redux-saga_0.9.5.js:26:15,18: property `type` | Property not found in `Array` | const { type } = action;', 'line 7 should have the right format'); 21 | 22 | q.end(); 23 | }); 24 | 25 | t.test('with fixture/2.json', (q) => { 26 | const lines = formatQuickfix(F2) 27 | 28 | q.equals(lines.length, 3, 'there should be the appropriate number of lines'); 29 | 30 | // q.equals(lines[0], '/Projects/test/interface/redux-saga_0.9.5.js:25:5,28: [LIB] | $npm$ReduxSaga$PutEffect | Application of polymorphic type needs . (Can use `*` for inferrable ones)', 'line 1 should have the right format'); 31 | // q.equals(lines[1], '/Projects/test/interface/redux-saga_0.9.5.js:26:5,29: [LIB] | $npm$ReduxSaga$CallEffect | Application of polymorphic type needs . (Can use `*` for inferrable ones)', 'line 2 should have the right format'); 32 | // q.equals(lines[2], '/Projects/test/test_redux-saga_0.9.5.js:26:15,18: property `type` | Property not found in `Array` | const { type } = action;', 'line 3 should have the right format'); 33 | 34 | q.end(); 35 | }); 36 | 37 | t.end(); 38 | }); 39 | --------------------------------------------------------------------------------