Skip to content

Commit

Permalink
feat: support eslint flat config (#282)
Browse files Browse the repository at this point in the history
* feat: support eslint flat config

* chore: add test

* feat: add useFlatConfig option

* chore: update

* docs: useFlatConfig option for eslint

* chore: update changesets
  • Loading branch information
hyoban authored Jan 31, 2024
1 parent bc4fa05 commit c5d5109
Show file tree
Hide file tree
Showing 14 changed files with 440 additions and 11 deletions.
5 changes: 5 additions & 0 deletions .changeset/new-kings-breathe.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'vite-plugin-checker': patch
---

support eslint flat config
1 change: 1 addition & 0 deletions docs/checkers/eslint.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,5 +35,6 @@ Advanced object configuration table of `options.eslint`
| field | Type | Default value | Description |
| :----------------- | ---------------------------------------------------------------------------------------------------------- | ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| lintCommand | `string` | This value is required | `lintCommand` will be executed at build mode, and will also be used as default config for dev mode when `eslint.dev.eslint` is nullable. |
| useFlatConfig | `boolean` | `false` | If `true`, the plugin will use the new [flat config](https://eslint.org/docs/latest/use/configure/configuration-files-new) of ESLint. |
| dev.overrideConfig | [`ESLint.Options`](https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/eslint/index.d.ts) | `undefined` | **(Only in dev mode)** You can override the options of the translated from `lintCommand`. Config priority: `const eslint = new ESLint({cwd: root, ...translatedOptions, ...pluginConfig.eslint.dev?.overrideConfig, })`. |
| dev.logLevel | `('error' \| 'warning')[]` | `['error', 'warning']` | **(Only in dev mode)** Which level of ESLint should be emitted to terminal and overlay in dev mode |
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@
"@typescript-eslint/parser": "^5.59.0",
"chalk": "^4.1.1",
"cross-env": "^7.0.3",
"eslint": "^8.11.0",
"eslint": "^8.51.0",
"eslint-config-alloy": "^4.5.1",
"execa": "^5.1.1",
"fast-glob": "^3.2.7",
Expand Down
17 changes: 16 additions & 1 deletion packages/vite-plugin-checker/src/checkers/eslint/main.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import Module from 'node:module'
import chokidar from 'chokidar'
import { ESLint } from 'eslint'
import path from 'path'
Expand All @@ -21,6 +22,7 @@ import { translateOptions } from './cli.js'
import { options as optionator } from './options.js'

const __filename = fileURLToPath(import.meta.url)
const require = Module.createRequire(import.meta.url)

const manager = new FileDiagnosticManager()
let createServeAndBuild
Expand Down Expand Up @@ -63,7 +65,20 @@ const createDiagnostic: CreateDiagnostic<'eslint'> = (pluginConfig) => {
...translatedOptions,
...pluginConfig.eslint.dev?.overrideConfig,
}
const eslint = new ESLint(eslintOptions)

let eslint: ESLint
if (pluginConfig.eslint.useFlatConfig) {
const { FlatESLint, shouldUseFlatConfig } = require('eslint/use-at-your-own-risk')
if (shouldUseFlatConfig?.()) {
eslint = new FlatESLint({
cwd: root,
})
} else {
throw Error('Please upgrade your eslint to latest version to use `useFlatConfig` option.')
}
} else {
eslint = new ESLint(eslintOptions)
}

const dispatchDiagnostics = () => {
const diagnostics = filterLogLevel(manager.getDiagnostics(), logLevel)
Expand Down
4 changes: 4 additions & 0 deletions packages/vite-plugin-checker/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,10 @@ export type EslintConfig =
* default config for dev mode when options.eslint.dev.eslint is nullable.
*/
lintCommand: string
/**
* @default false
*/
useFlatConfig?: boolean
dev?: Partial<{
/** You can override the options of translated from lintCommand. */
overrideConfig: ESLint.Options
Expand Down
27 changes: 27 additions & 0 deletions playground/eslint-flat/__tests__/__snapshots__/test.spec.ts.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html

exports[`eslint > serve > get initial error and subsequent error 1`] = `"[{\\"checkerId\\":\\"ESLint\\",\\"frame\\":\\" > 1 | const a = /\\"hello/\\"/n | ^/n > 2 |/n | ^\\",\\"id\\":\\"<PROJECT_ROOT>/playground-temp/eslint-flat/src/index.js\\",\\"level\\":1,\\"loc\\":{\\"column\\":18,\\"file\\":\\"<PROJECT_ROOT>/playground-temp/eslint-flat/src/index.js\\",\\"line\\":1},\\"message\\":\\"Missing semicolon. (semi)\\",\\"stack\\":\\"\\"}]"`;

exports[`eslint > serve > get initial error and subsequent error 2`] = `
" ERROR(ESLint) Missing semicolon. (semi)
FILE <PROJECT_ROOT>/playground-temp/eslint-flat/src/index.js:1:18
> 1 | const a = \\"hello\\"
| ^
> 2 |
| ^
[ESLint] Found 1 error and 0 warning"
`;
exports[`eslint > serve > get initial error and subsequent error 3`] = `"[{\\"checkerId\\":\\"ESLint\\",\\"frame\\":\\" > 1 | const a = /\\"hello/\\"/n | ^/n > 2 |/n | ^\\",\\"id\\":\\"<PROJECT_ROOT>/playground-temp/eslint-flat/src/index.js\\",\\"level\\":1,\\"loc\\":{\\"column\\":18,\\"file\\":\\"<PROJECT_ROOT>/playground-temp/eslint-flat/src/index.js\\",\\"line\\":1},\\"message\\":\\"Missing semicolon. (semi)\\",\\"stack\\":\\"\\"}]"`;
exports[`eslint > serve > get initial error and subsequent error 4`] = `
" ERROR(ESLint) Missing semicolon. (semi)
FILE <PROJECT_ROOT>/playground-temp/eslint-flat/src/index.js:1:18
> 1 | const a = \\"hello\\"
| ^
> 2 |
| ^
[ESLint] Found 1 error and 0 warning"
`;
39 changes: 39 additions & 0 deletions playground/eslint-flat/__tests__/test.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import stringify from 'fast-json-stable-stringify'
import { describe, expect, it } from 'vitest'
import {
diagnostics,
editFile,
expectStderrContains,
isBuild,
isServe,
log,
resetReceivedLog,
sleepForEdit,
sleepForServerReady,
stripedLog,
} from '../../testUtils'

describe('eslint', () => {
describe.runIf(isServe)('serve', () => {
it('get initial error and subsequent error', async () => {
await sleepForServerReady()
expect(stringify(diagnostics)).toMatchSnapshot()
expect(stripedLog).toMatchSnapshot()

console.log('-- edit error file --')
resetReceivedLog()
editFile('src/index.js', (code) => code.replace(`Hello`, `Hello~`))
await sleepForEdit()
expect(stringify(diagnostics)).toMatchSnapshot()
expect(stripedLog).toMatchSnapshot()
})
})

describe.runIf(isBuild)('build', () => {
const expectedMsg = 'Missing semicolon'

it('should fail', async () => {
expectStderrContains(log, expectedMsg)
})
})
})
8 changes: 8 additions & 0 deletions playground/eslint-flat/eslint.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export default [
{
files: ["src/**/*"],
rules: {
semi: 'error',
},
},
]
13 changes: 13 additions & 0 deletions playground/eslint-flat/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="src/favicon.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite App</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/index.js"></script>
</body>
</html>
17 changes: 17 additions & 0 deletions playground/eslint-flat/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"private": true,
"name": "@playground/eslint-flat",
"type": "module",
"version": "0.0.0",
"scripts": {
"dev": "vite",
"build": "vite build",
"serve": "vite preview",
"lint": "eslint ."
},
"devDependencies": {
"eslint": "^8.51.0",
"vite": "^4.3.0",
"vite-plugin-checker": "workspace:*"
}
}
1 change: 1 addition & 0 deletions playground/eslint-flat/src/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
const a = "hello"
19 changes: 19 additions & 0 deletions playground/eslint-flat/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"compilerOptions": {
"target": "ESNext",
"lib": ["DOM", "DOM.Iterable", "ESNext"],
"types": ["vite/client"],
"allowJs": false,
"skipLibCheck": false,
"esModuleInterop": false,
"allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"module": "ESNext",
"moduleResolution": "Node",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true
},
"include": ["./src"]
}
13 changes: 13 additions & 0 deletions playground/eslint-flat/vite.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { defineConfig } from 'vite'
import checker from 'vite-plugin-checker'

export default defineConfig({
plugins: [
checker({
eslint: {
lintCommand: 'eslint "./src/**/*.js"',
useFlatConfig: true,
},
}),
],
})
Loading

0 comments on commit c5d5109

Please sign in to comment.