├── .editorconfig ├── .github └── workflows │ ├── bb.yml │ └── main.yml ├── .gitignore ├── .npmrc ├── .prettierignore ├── index.js ├── lib └── index.js ├── license ├── package.json ├── readme.md ├── screenshot.png ├── test.js └── tsconfig.json /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | -------------------------------------------------------------------------------- /.github/workflows/bb.yml: -------------------------------------------------------------------------------- 1 | name: bb 2 | on: 3 | issues: 4 | types: [opened, reopened, edited, closed, labeled, unlabeled] 5 | pull_request_target: 6 | types: [opened, reopened, edited, closed, labeled, unlabeled] 7 | jobs: 8 | main: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: unifiedjs/beep-boop-beta@main 12 | with: 13 | repo-token: ${{secrets.GITHUB_TOKEN}} 14 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: main 2 | on: 3 | - pull_request 4 | - push 5 | jobs: 6 | main: 7 | name: '${{matrix.node}} on ${{matrix.os}}' 8 | runs-on: ${{matrix.os}} 9 | steps: 10 | - uses: actions/checkout@v3 11 | - uses: actions/setup-node@v3 12 | with: 13 | node-version: ${{matrix.node}} 14 | - run: npm install 15 | - run: npm test 16 | - uses: codecov/codecov-action@v3 17 | strategy: 18 | matrix: 19 | os: 20 | - ubuntu-latest 21 | - windows-latest 22 | node: 23 | - lts/gallium 24 | - node 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | *.d.ts 3 | *.log 4 | coverage/ 5 | node_modules/ 6 | yarn.lock 7 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | package-lock=false 2 | ignore-scripts=true 3 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | coverage/ 2 | *.md 3 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @typedef {import('./lib/index.js').LintResult} LintResult 3 | */ 4 | 5 | export {toESLint} from './lib/index.js' 6 | -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @typedef {import('eslint').ESLint.LintResult} LintResult 3 | * @typedef {import('eslint').Linter.LintMessage} LintMessage 4 | * @typedef {import('vfile').VFile} VFile 5 | * @typedef {import('vfile-message').VFileMessage} VFileMessage 6 | */ 7 | 8 | import {statistics} from 'vfile-statistics' 9 | 10 | /** 11 | * Turn virtual files into a ESLint results that can be passed directly to an 12 | * ESLint formatter. 13 | * 14 | * @param {Array} files 15 | * Virtual files. 16 | * @returns {Array} 17 | * Lint results. 18 | */ 19 | export function toESLint(files) { 20 | return files.map(function (file) { 21 | return transformFile(file) 22 | }) 23 | } 24 | 25 | /** 26 | * Map a file. 27 | * 28 | * @param {VFile} file 29 | * Virtual file. 30 | * @returns {LintResult} 31 | * ESLint result. 32 | */ 33 | function transformFile(file) { 34 | const stats = statistics(file) 35 | 36 | return { 37 | filePath: file.path, 38 | messages: file.messages.map(function (message) { 39 | return transformMessage(message) 40 | }), 41 | fatalErrorCount: stats.fatal, 42 | errorCount: stats.fatal, 43 | warningCount: stats.nonfatal, 44 | fixableErrorCount: 0, 45 | fixableWarningCount: 0, 46 | usedDeprecatedRules: [], 47 | suppressedMessages: [] 48 | } 49 | } 50 | 51 | /** 52 | * Map a message. 53 | * 54 | * @param {VFileMessage} message 55 | * Virtual message. 56 | * @returns {LintMessage} 57 | * ESLint message. 58 | */ 59 | function transformMessage(message) { 60 | /** @type {VFileMessage['place']} */ 61 | // @ts-expect-error `position` is the old name. 62 | const position = message.place || message.position 63 | 64 | const end = (position && 'end' in position ? position.end : undefined) || { 65 | line: 0, 66 | column: 0 67 | } 68 | 69 | return { 70 | fatal: message.fatal === true ? true : undefined, 71 | severity: message.fatal ? 2 : 1, 72 | ruleId: [message.source, message.ruleId].filter(Boolean).join(':') || null, 73 | line: message.line || 0, 74 | column: message.column || 0, 75 | endLine: end.line || undefined, 76 | endColumn: end.column || undefined, 77 | message: message.reason 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /license: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) Sindre Sorhus (sindresorhus.com) 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 13 | all 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 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vfile-to-eslint", 3 | "version": "4.0.0", 4 | "description": "vfile utility to convert to ESLint formatter compatible output", 5 | "license": "MIT", 6 | "keywords": [ 7 | "vfile", 8 | "vfile-util", 9 | "vfile-reporter", 10 | "util", 11 | "utility", 12 | "virtual", 13 | "file", 14 | "reporter", 15 | "fortmatter", 16 | "eslint", 17 | "convert" 18 | ], 19 | "repository": "vfile/vfile-to-eslint", 20 | "bugs": "https://github.com/vfile/vfile-to-eslint/issues", 21 | "funding": { 22 | "type": "opencollective", 23 | "url": "https://opencollective.com/unified" 24 | }, 25 | "author": "Sindre Sorhus (https://sindresorhus.com)", 26 | "contributors": [ 27 | "Sindre Sorhus (https://sindresorhus.com)", 28 | "Titus Wormer (https://wooorm.com)" 29 | ], 30 | "sideEffects": false, 31 | "type": "module", 32 | "exports": "./index.js", 33 | "files": [ 34 | "lib/", 35 | "index.d.ts", 36 | "index.js" 37 | ], 38 | "dependencies": { 39 | "@types/eslint": "^8.0.0", 40 | "vfile": "^6.0.0", 41 | "vfile-message": "^4.0.0", 42 | "vfile-statistics": "^3.0.0" 43 | }, 44 | "devDependencies": { 45 | "@types/node": "^20.0.0", 46 | "c8": "^8.0.0", 47 | "prettier": "^2.0.0", 48 | "remark-cli": "^11.0.0", 49 | "remark-preset-wooorm": "^9.0.0", 50 | "type-coverage": "^2.0.0", 51 | "typescript": "^5.0.0", 52 | "xo": "^0.54.0" 53 | }, 54 | "scripts": { 55 | "prepack": "npm run build && npm run format", 56 | "build": "tsc --build --clean && tsc --build && type-coverage", 57 | "format": "remark . -qfo && prettier . -w --loglevel warn && xo --fix", 58 | "test-api": "node --conditions development test.js", 59 | "test-coverage": "c8 --100 --reporter lcov npm run test-api", 60 | "test": "npm run build && npm run format && npm run test-coverage" 61 | }, 62 | "prettier": { 63 | "bracketSpacing": false, 64 | "semi": false, 65 | "singleQuote": true, 66 | "tabWidth": 2, 67 | "trailingComma": "none", 68 | "useTabs": false 69 | }, 70 | "remarkConfig": { 71 | "plugins": [ 72 | "remark-preset-wooorm" 73 | ] 74 | }, 75 | "typeCoverage": { 76 | "atLeast": 100, 77 | "detail": true, 78 | "ignoreCatch": true, 79 | "strict": true 80 | }, 81 | "xo": { 82 | "prettier": true 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # vfile-to-eslint 2 | 3 | [![Build][build-badge]][build] 4 | [![Coverage][coverage-badge]][coverage] 5 | [![Downloads][downloads-badge]][downloads] 6 | [![Size][size-badge]][size] 7 | [![Sponsors][sponsors-badge]][collective] 8 | [![Backers][backers-badge]][collective] 9 | [![Chat][chat-badge]][chat] 10 | 11 | Convert [VFiles][vfile] to [ESLint][] formatter compatible output. 12 | 13 | ![][screenshot] 14 | 15 | ## Contents 16 | 17 | * [What is this?](#what-is-this) 18 | * [When should I use this?](#when-should-i-use-this) 19 | * [Install](#install) 20 | * [Use](#use) 21 | * [API](#api) 22 | * [`toESLint(files)`](#toeslintfiles) 23 | * [Types](#types) 24 | * [Compatibility](#compatibility) 25 | * [Contribute](#contribute) 26 | * [License](#license) 27 | 28 | ## What is this? 29 | 30 | This package turns a file into the `LintResult` schema that ESLint uses for its 31 | formatters 32 | 33 | ## When should I use this? 34 | 35 | While vfile and ESLint results are different, they do overlap in some places. 36 | This package can be used to integrate with ESLint in those cases. 37 | In other cases, a custom vfile utility might be a better solution. 38 | 39 | ## Install 40 | 41 | This package is [ESM only][esm]. 42 | In Node.js (version 16+), install with [npm][]: 43 | 44 | ```sh 45 | npm install vfile-to-eslint 46 | ``` 47 | 48 | In Deno with [`esm.sh`][esmsh]: 49 | 50 | ```js 51 | import {toESLint} from 'https://esm.sh/vfile-to-eslint@4' 52 | ``` 53 | 54 | In browsers with [`esm.sh`][esmsh]: 55 | 56 | ```html 57 | 60 | ``` 61 | 62 | ## Use 63 | 64 | ```js 65 | import {remark} from 'remark' 66 | import remarkPresetLintRecommended from 'remark-preset-lint-recommended' 67 | import eslintFormatterPretty from 'eslint-formatter-pretty' 68 | import {toESLint} from 'vfile-to-eslint' 69 | 70 | const file = await remark() 71 | .use(remarkPresetLintRecommended) 72 | .process('## Hello world!') 73 | 74 | console.log(eslintFormatterPretty(toESLint([file]))) 75 | ``` 76 | 77 | ## API 78 | 79 | This package exports the identifier [`toESLint`][api-to-eslint]. 80 | There is no default export. 81 | 82 | ### `toESLint(files)` 83 | 84 | Turn virtual files into a ESLint results that can be passed directly to an 85 | [ESLint formatter][eslint-formatter]. 86 | 87 | ###### Parameters 88 | 89 | * `files` ([`Array`][vfile]) 90 | — list of files 91 | 92 | ###### Returns 93 | 94 | Lint results ([`Array`][lintresult]) 95 | 96 | ## Types 97 | 98 | This package is fully typed with [TypeScript][]. 99 | It exports the additional type [`LintResult`][lintresult]. 100 | 101 | ## Compatibility 102 | 103 | Projects maintained by the unified collective are compatible with maintained 104 | versions of Node.js. 105 | 106 | When we cut a new major release, we drop support for unmaintained versions of 107 | Node. 108 | This means we try to keep the current release line, `vfile-to-eslint@^4`, 109 | compatible with Node.js 16. 110 | 111 | ## Contribute 112 | 113 | See [`contributing.md`][contributing] in [`vfile/.github`][health] for ways to 114 | get started. 115 | See [`support.md`][support] for ways to get help. 116 | 117 | This project has a [code of conduct][coc]. 118 | By interacting with this repository, organization, or community you agree to 119 | abide by its terms. 120 | 121 | ## License 122 | 123 | [MIT][license] © [Sindre Sorhus][author] 124 | 125 | 126 | 127 | [build-badge]: https://github.com/vfile/vfile-to-eslint/workflows/main/badge.svg 128 | 129 | [build]: https://github.com/vfile/vfile-to-eslint/actions 130 | 131 | [coverage-badge]: https://img.shields.io/codecov/c/github/vfile/vfile-to-eslint.svg 132 | 133 | [coverage]: https://codecov.io/github/vfile/vfile-to-eslint 134 | 135 | [downloads-badge]: https://img.shields.io/npm/dm/vfile-to-eslint.svg 136 | 137 | [downloads]: https://www.npmjs.com/package/vfile-to-eslint 138 | 139 | [size-badge]: https://img.shields.io/badge/dynamic/json?label=minzipped%20size&query=$.size.compressedSize&url=https://deno.bundlejs.com/?q=vfile-to-eslint 140 | 141 | [size]: https://bundlejs.com/?q=vfile-to-eslint 142 | 143 | [sponsors-badge]: https://opencollective.com/unified/sponsors/badge.svg 144 | 145 | [backers-badge]: https://opencollective.com/unified/backers/badge.svg 146 | 147 | [collective]: https://opencollective.com/unified 148 | 149 | [chat-badge]: https://img.shields.io/badge/chat-discussions-success.svg 150 | 151 | [chat]: https://github.com/vfile/vfile/discussions 152 | 153 | [npm]: https://docs.npmjs.com/cli/install 154 | 155 | [esm]: https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c 156 | 157 | [esmsh]: https://esm.sh 158 | 159 | [typescript]: https://www.typescriptlang.org 160 | 161 | [contributing]: https://github.com/vfile/.github/blob/main/contributing.md 162 | 163 | [support]: https://github.com/vfile/.github/blob/main/support.md 164 | 165 | [health]: https://github.com/vfile/.github 166 | 167 | [coc]: https://github.com/vfile/.github/blob/main/code-of-conduct.md 168 | 169 | [license]: license 170 | 171 | [author]: https://sindresorhus.com 172 | 173 | [screenshot]: screenshot.png 174 | 175 | [vfile]: https://github.com/vfile/vfile 176 | 177 | [eslint]: https://eslint.org 178 | 179 | [eslint-formatter]: https://npms.io/search?term=eslint-formatter 180 | 181 | [lintresult]: https://eslint.org/docs/latest/integrate/nodejs-api#-lintresult-type 182 | 183 | [api-to-eslint]: #toeslintfiles 184 | -------------------------------------------------------------------------------- /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vfile/vfile-to-eslint/9c18335aabd815d6131495f5e4986c222e259fc6/screenshot.png -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- 1 | import assert from 'node:assert/strict' 2 | import test from 'node:test' 3 | import {VFile} from 'vfile' 4 | import {toESLint} from 'vfile-to-eslint' 5 | 6 | test('toESLint', async function () { 7 | assert.deepEqual( 8 | Object.keys(await import('vfile-to-eslint')).sort(), 9 | ['toESLint'], 10 | 'should expose the public api' 11 | ) 12 | 13 | const file = new VFile({path: '~/example.md'}) 14 | 15 | file.info('?') 16 | 17 | file.info('This is perfect', {line: 5, column: 3}, 'alpha:bravo') 18 | 19 | file.message('This should be fixed', { 20 | start: {line: 3, column: 5}, 21 | end: {line: 3, column: 7} 22 | }) 23 | 24 | try { 25 | file.fail('This is horrible', { 26 | place: { 27 | start: {line: 2, column: 1}, 28 | end: {line: 2, column: 8} 29 | } 30 | }) 31 | } catch {} 32 | 33 | assert.deepEqual(toESLint([file]), [ 34 | { 35 | filePath: '~/example.md', 36 | messages: [ 37 | { 38 | fatal: undefined, 39 | severity: 1, 40 | ruleId: null, 41 | line: 0, 42 | column: 0, 43 | endLine: undefined, 44 | endColumn: undefined, 45 | message: '?' 46 | }, 47 | { 48 | fatal: undefined, 49 | severity: 1, 50 | ruleId: 'alpha:bravo', 51 | line: 5, 52 | column: 3, 53 | endLine: undefined, 54 | endColumn: undefined, 55 | message: 'This is perfect' 56 | }, 57 | { 58 | fatal: undefined, 59 | severity: 1, 60 | ruleId: null, 61 | line: 3, 62 | column: 5, 63 | endLine: 3, 64 | endColumn: 7, 65 | message: 'This should be fixed' 66 | }, 67 | { 68 | fatal: true, 69 | severity: 2, 70 | ruleId: null, 71 | line: 2, 72 | column: 1, 73 | endLine: 2, 74 | endColumn: 8, 75 | message: 'This is horrible' 76 | } 77 | ], 78 | fatalErrorCount: 1, 79 | errorCount: 1, 80 | warningCount: 3, 81 | fixableErrorCount: 0, 82 | fixableWarningCount: 0, 83 | usedDeprecatedRules: [], 84 | suppressedMessages: [] 85 | } 86 | ]) 87 | }) 88 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "checkJs": true, 4 | "customConditions": ["development"], 5 | "declaration": true, 6 | "emitDeclarationOnly": true, 7 | "exactOptionalPropertyTypes": true, 8 | "lib": ["es2022"], 9 | "module": "node16", 10 | "strict": true, 11 | "target": "es2022" 12 | }, 13 | "exclude": ["coverage/", "node_modules/"], 14 | "include": ["**/*.js"] 15 | } 16 | --------------------------------------------------------------------------------