Skip to content

Commit

Permalink
fix: ESLint can report multiple errors
Browse files Browse the repository at this point in the history
  • Loading branch information
fi3ework committed Feb 6, 2022
1 parent 9226130 commit e1bcfb4
Show file tree
Hide file tree
Showing 14 changed files with 449 additions and 20 deletions.
22 changes: 16 additions & 6 deletions packages/vite-plugin-checker/__tests__/e2e/Sandbox/Sandbox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,24 @@ import invariant from 'tiny-invariant'
import { build, createServer, HMRPayload, ViteDevServer, CustomPayload } from 'vite'
import { Checker } from 'vite-plugin-checker'

import { expectStdoutNotContains, sleep, testDir } from '../testUtils'
import { expectStdoutNotContains, expectStderrContains, sleep, testDir } from '../testUtils'

import type { ElementHandleForTag } from 'playwright-chromium/types/structs'
let devServer: ViteDevServer
let binPath: string
export let log = ''
export let stripedLog = ''

export function proxyConsoleInTest() {
export function proxyConsoleInTest(accumulate = false) {
Checker.logger = [
(...args: any[]) => {
log = args[0].payload
stripedLog = strip(args[0].payload)
if (accumulate) {
log += args[0].payload
stripedLog += strip(args[0].payload)
} else {
log = args[0].payload
stripedLog = strip(args[0].payload)
}
},
]
}
Expand Down Expand Up @@ -156,15 +161,20 @@ export async function viteBuild({
cwd = process.cwd(),
}: {
unexpectedErrorMsg?: string | string[]
expectedErrorMsg?: string
expectedErrorMsg?: string | string[]
cwd?: string
} = {}) {
const promise = execa(binPath, ['build'], {
cwd: cwd ?? testDir,
})

if (expectedErrorMsg) {
await expect(promise).rejects.toThrow(expectedErrorMsg)
try {
await promise
throw new Error('Fail! Should failed with error message')
} catch (e) {
expectStderrContains(e.toString(), expectedErrorMsg)
}
return
}

Expand Down
7 changes: 7 additions & 0 deletions packages/vite-plugin-checker/__tests__/e2e/testUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,13 @@ export function editFile(
fs.writeFileSync(filePath, modified)
}

export function expectStderrContains(str: string, expectedErrorMsg: string | string[]) {
const errorMsgArr = Array.isArray(expectedErrorMsg) ? expectedErrorMsg : [expectedErrorMsg]
errorMsgArr.forEach((msg) => {
expect(str).toContain(msg)
})
}

export function expectStdoutNotContains(str: string, unexpectedErrorMsg: string | string[]) {
expect.objectContaining({
stdout: expect(str).not.toContain(unexpectedErrorMsg),
Expand Down
9 changes: 6 additions & 3 deletions packages/vite-plugin-checker/src/@runtime/overlay.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,8 +142,7 @@ code {
</div>
`

const errorItemTemplate = (checkerId: string) => `
<div class="message-item checker-${checkerId}">
const errorItemTemplate = (checkerId: string) => ` <div class="message-item checker-${checkerId}">
<pre class="message"><span class="plugin"></span><span class="message-body"></span></pre>
<pre class="file"></pre>
<pre class="frame"></pre>
Expand Down Expand Up @@ -192,8 +191,12 @@ export class ErrorOverlay extends HTMLElement {
}

public appendErrors({ errors, checkerId }: OverlayData) {
if (!errors.length) {
return
}

const toAppend: string[] = new Array(errors.length).fill(errorItemTemplate(checkerId))
this.root.querySelector('.message-list')!.innerHTML += toAppend
this.root.querySelector('.message-list')!.innerHTML += toAppend.join('')
errors.forEach((err, index) => {
codeframeRE.lastIndex = 0
const hasFrame = err.frame && codeframeRE.test(err.frame)
Expand Down
51 changes: 51 additions & 0 deletions packages/vite-plugin-checker/src/FileDiagnosticManager.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import type { NormalizedDiagnostic } from './logger'

class FileDiagnosticManager {
public diagnostics: NormalizedDiagnostic[] = []

public initWith(diagnostics: NormalizedDiagnostic[]) {
diagnostics.forEach((d) => {
this.diagnostics.push(d)
})
}

public getDiagnostics(fileName?: string) {
if (fileName) {
return this.diagnostics.filter((f) => f.id === fileName)
}

return this.diagnostics
}

// public get lastDiagnostic() {
// return this.diagnostics[this.diagnostics.length - 1]
// }

public setFile(fileName: string, next: NormalizedDiagnostic[] | null) {
for (let i = 0; i < this.diagnostics.length; i++) {
if (this.diagnostics[i].id === fileName) {
this.diagnostics.splice(i, 1)
i--
}
}

if (next?.length) {
this.diagnostics.push(...next)
}
}

// public updateFile(next: NormalizedDiagnostic[] | null) {
// for (let i = 0; i < this.diagnostics.length; i++) {
// if (this.diagnostics[i].loc?.start.line === fileName) {
// this.diagnostics.splice(i, 1)
// i--
// }
// }

// if (next?.length) {
// this.diagnostics.push(...next)
// }
// }
}

export { FileDiagnosticManager }
31 changes: 20 additions & 11 deletions packages/vite-plugin-checker/src/checkers/eslint/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ import {
} from '../../logger'
import { ACTION_TYPES } from '../../types'
import { translateOptions } from './cli'
import { FileDiagnosticManager } from '../../FileDiagnosticManager'

const manager = new FileDiagnosticManager()

import type { CreateDiagnostic } from '../../types'
const createDiagnostic: CreateDiagnostic<'eslint'> = (pluginConfig) => {
Expand Down Expand Up @@ -56,19 +59,23 @@ const createDiagnostic: CreateDiagnostic<'eslint'> = (pluginConfig) => {
// : pluginConfig.eslint.files
// const paths = config.

let diagnosticsCache: NormalizedDiagnostic[] = []
// let diagnosticsCache: NormalizedDiagnostic[] = []

const dispatchDiagnostics = () => {
diagnosticsCache.forEach((n) => {
consoleLog(diagnosticToTerminalLog(n, 'ESLint'))
const diagnostics = manager.getDiagnostics()
diagnostics.forEach((d) => {
consoleLog(diagnosticToTerminalLog(d, 'ESLint'))
})

const lastErr = diagnosticsCache[0]
// const lastErr = diagnosticsCache[0]

if (overlay) {
parentPort?.postMessage({
type: ACTION_TYPES.overlayError,
payload: toViteCustomPayload('eslint', lastErr ? [diagnosticToViteError(lastErr)] : []),
payload: toViteCustomPayload(
'eslint',
diagnostics.map((d) => diagnosticToViteError(d))
),

// payload: lastErr
// ? {
Expand All @@ -82,18 +89,19 @@ const createDiagnostic: CreateDiagnostic<'eslint'> = (pluginConfig) => {

const handleFileChange = async (filePath: string, type: 'change' | 'unlink') => {
// if (!extensions.includes(path.extname(filePath))) return
const absPath = path.resolve(root, filePath)

if (type === 'unlink') {
const absPath = path.resolve(root, filePath)
diagnosticsCache = diagnosticsCache.filter((d) => d.id !== absPath)
manager.setFile(absPath, [])
// diagnosticsCache = diagnosticsCache.filter((d) => d.id !== absPath)
} else if (type === 'change') {
console.log(filePath)
const diagnosticsOfChangedFile = await eslint.lintFiles(filePath)
const newDiagnostics = diagnosticsOfChangedFile
.map((d) => normalizeEslintDiagnostic(d))
.flat(1)
const absPath = diagnosticsOfChangedFile[0].filePath
diagnosticsCache = diagnosticsCache.filter((d) => d.id !== absPath).concat(newDiagnostics)
// const absPath = diagnosticsOfChangedFile[0].filePath
// diagnosticsCache = diagnosticsCache.filter((d) => d.id !== absPath).concat(newDiagnostics)
manager.setFile(absPath, newDiagnostics)
}

dispatchDiagnostics()
Expand All @@ -102,7 +110,8 @@ const createDiagnostic: CreateDiagnostic<'eslint'> = (pluginConfig) => {
// initial lint
const files = options._.slice(1)
const diagnostics = await eslint.lintFiles(files)
diagnosticsCache = diagnostics.map((p) => normalizeEslintDiagnostic(p)).flat(1)
manager.initWith(diagnostics.map((p) => normalizeEslintDiagnostic(p)).flat(1))
// diagnosticsCache = diagnostics.map((p) => normalizeEslintDiagnostic(p)).flat(1)
dispatchDiagnostics()

// watch lint
Expand Down
148 changes: 148 additions & 0 deletions playground/multiple/__tests__/__snapshots__/test.spec.ts.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`multiple serve get initial error and subsequent error 1`] = `"[{\\"frame\\":\\" 1 | import { text } from './text'/n 2 |/n > 3 | var hello1: number = 'Hello1'/n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^/n 4 | var hello2: boolean = 'Hello2'/n 5 |/n 6 | const rootDom = document.querySelector('#root')!\\",\\"id\\":\\"<PROJECT_ROOT>/temp/multiple/src/main.ts\\",\\"loc\\":{\\"column\\":1,\\"file\\":\\"<PROJECT_ROOT>/temp/multiple/src/main.ts\\",\\"line\\":3},\\"message\\":\\"Unexpected var, use let or const instead.\\",\\"plugin\\":\\"ESLint\\",\\"stack\\":\\"\\"},{\\"frame\\":\\" 2 |/n 3 | var hello1: number = 'Hello1'/n > 4 | var hello2: boolean = 'Hello2'/n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^/n 5 |/n 6 | const rootDom = document.querySelector('#root')!/n 7 | rootDom.innerHTML = hello1 + text\\",\\"id\\":\\"<PROJECT_ROOT>/temp/multiple/src/main.ts\\",\\"loc\\":{\\"column\\":1,\\"file\\":\\"<PROJECT_ROOT>/temp/multiple/src/main.ts\\",\\"line\\":4},\\"message\\":\\"Unexpected var, use let or const instead.\\",\\"plugin\\":\\"ESLint\\",\\"stack\\":\\"\\"},{\\"frame\\":\\" 1 | import { text } from './text'/n 2 |/n > 3 | var hello1: number = 'Hello1'/n | ^^^^^^/n 4 | var hello2: boolean = 'Hello2'/n 5 |/n 6 | const rootDom = document.querySelector('#root')!\\",\\"id\\":\\"<PROJECT_ROOT>/temp/multiple/src/main.ts\\",\\"loc\\":{\\"column\\":5,\\"file\\":\\"<PROJECT_ROOT>/temp/multiple/src/main.ts\\",\\"line\\":3},\\"message\\":\\"Type 'string' is not assignable to type 'number'.\\",\\"plugin\\":\\"TypeScript\\",\\"stack\\":\\"\\"},{\\"frame\\":\\" 2 |/n 3 | var hello1: number = 'Hello1'/n > 4 | var hello2: boolean = 'Hello2'/n | ^^^^^^/n 5 |/n 6 | const rootDom = document.querySelector('#root')!/n 7 | rootDom.innerHTML = hello1 + text\\",\\"id\\":\\"<PROJECT_ROOT>/temp/multiple/src/main.ts\\",\\"loc\\":{\\"column\\":5,\\"file\\":\\"<PROJECT_ROOT>/temp/multiple/src/main.ts\\",\\"line\\":4},\\"message\\":\\"Type 'string' is not assignable to type 'boolean'.\\",\\"plugin\\":\\"TypeScript\\",\\"stack\\":\\"\\"}]"`;

exports[`multiple serve get initial error and subsequent error 2`] = `
" ERROR(ESLint) Unexpected var, use let or const instead.
FILE <PROJECT_ROOT>/temp/multiple/src/main.ts:3:1
1 | import { text } from './text'
2 |
> 3 | var hello1: number = 'Hello1'
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
4 | var hello2: boolean = 'Hello2'
5 |
6 | const rootDom = document.querySelector('#root')!
ERROR(ESLint) Unexpected var, use let or const instead.
FILE <PROJECT_ROOT>/temp/multiple/src/main.ts:4:1
2 |
3 | var hello1: number = 'Hello1'
> 4 | var hello2: boolean = 'Hello2'
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
5 |
6 | const rootDom = document.querySelector('#root')!
7 | rootDom.innerHTML = hello1 + text
ERROR(TypeScript) Type 'string' is not assignable to type 'number'.
FILE <PROJECT_ROOT>/temp/multiple/src/main.ts:3:5
1 | import { text } from './text'
2 |
> 3 | var hello1: number = 'Hello1'
| ^^^^^^
4 | var hello2: boolean = 'Hello2'
5 |
6 | const rootDom = document.querySelector('#root')!
ERROR(TypeScript) Type 'string' is not assignable to type 'boolean'.
FILE <PROJECT_ROOT>/temp/multiple/src/main.ts:4:5
2 |
3 | var hello1: number = 'Hello1'
> 4 | var hello2: boolean = 'Hello2'
| ^^^^^^
5 |
6 | const rootDom = document.querySelector('#root')!
7 | rootDom.innerHTML = hello1 + text
Found 2 errors. Watching for file changes."
`;
exports[`multiple serve get initial error and subsequent error 3`] = `"[{\\"frame\\":\\" 1 | import { text } from './text'/n 2 |/n > 3 | var hello1: number = 'Hello1~'/n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^/n 4 | var hello2: boolean = 'Hello2'/n 5 |/n 6 | const rootDom = document.querySelector('#root')!\\",\\"id\\":\\"<PROJECT_ROOT>/temp/multiple/src/main.ts\\",\\"loc\\":{\\"column\\":1,\\"file\\":\\"<PROJECT_ROOT>/temp/multiple/src/main.ts\\",\\"line\\":3},\\"message\\":\\"Unexpected var, use let or const instead.\\",\\"plugin\\":\\"ESLint\\",\\"stack\\":\\"\\"},{\\"frame\\":\\" 2 |/n 3 | var hello1: number = 'Hello1~'/n > 4 | var hello2: boolean = 'Hello2'/n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^/n 5 |/n 6 | const rootDom = document.querySelector('#root')!/n 7 | rootDom.innerHTML = hello1 + text\\",\\"id\\":\\"<PROJECT_ROOT>/temp/multiple/src/main.ts\\",\\"loc\\":{\\"column\\":1,\\"file\\":\\"<PROJECT_ROOT>/temp/multiple/src/main.ts\\",\\"line\\":4},\\"message\\":\\"Unexpected var, use let or const instead.\\",\\"plugin\\":\\"ESLint\\",\\"stack\\":\\"\\"},{\\"frame\\":\\" 1 | import { text } from './text'/n 2 |/n > 3 | var hello1: number = 'Hello1~'/n | ^^^^^^/n 4 | var hello2: boolean = 'Hello2'/n 5 |/n 6 | const rootDom = document.querySelector('#root')!\\",\\"id\\":\\"<PROJECT_ROOT>/temp/multiple/src/main.ts\\",\\"loc\\":{\\"column\\":5,\\"file\\":\\"<PROJECT_ROOT>/temp/multiple/src/main.ts\\",\\"line\\":3},\\"message\\":\\"Type 'string' is not assignable to type 'number'.\\",\\"plugin\\":\\"TypeScript\\",\\"stack\\":\\"\\"},{\\"frame\\":\\" 2 |/n 3 | var hello1: number = 'Hello1~'/n > 4 | var hello2: boolean = 'Hello2'/n | ^^^^^^/n 5 |/n 6 | const rootDom = document.querySelector('#root')!/n 7 | rootDom.innerHTML = hello1 + text\\",\\"id\\":\\"<PROJECT_ROOT>/temp/multiple/src/main.ts\\",\\"loc\\":{\\"column\\":5,\\"file\\":\\"<PROJECT_ROOT>/temp/multiple/src/main.ts\\",\\"line\\":4},\\"message\\":\\"Type 'string' is not assignable to type 'boolean'.\\",\\"plugin\\":\\"TypeScript\\",\\"stack\\":\\"\\"}]"`;
exports[`multiple serve get initial error and subsequent error 4`] = `
" ERROR(ESLint) Unexpected var, use let or const instead.
FILE <PROJECT_ROOT>/temp/multiple/src/main.ts:3:1
1 | import { text } from './text'
2 |
> 3 | var hello1: number = 'Hello1~'
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
4 | var hello2: boolean = 'Hello2'
5 |
6 | const rootDom = document.querySelector('#root')!
ERROR(ESLint) Unexpected var, use let or const instead.
FILE <PROJECT_ROOT>/temp/multiple/src/main.ts:4:1
2 |
3 | var hello1: number = 'Hello1~'
> 4 | var hello2: boolean = 'Hello2'
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
5 |
6 | const rootDom = document.querySelector('#root')!
7 | rootDom.innerHTML = hello1 + text
ERROR(TypeScript) Type 'string' is not assignable to type 'number'.
FILE <PROJECT_ROOT>/temp/multiple/src/main.ts:3:5
1 | import { text } from './text'
2 |
> 3 | var hello1: number = 'Hello1~'
| ^^^^^^
4 | var hello2: boolean = 'Hello2'
5 |
6 | const rootDom = document.querySelector('#root')!
ERROR(TypeScript) Type 'string' is not assignable to type 'boolean'.
FILE <PROJECT_ROOT>/temp/multiple/src/main.ts:4:5
2 |
3 | var hello1: number = 'Hello1~'
> 4 | var hello2: boolean = 'Hello2'
| ^^^^^^
5 |
6 | const rootDom = document.querySelector('#root')!
7 | rootDom.innerHTML = hello1 + text
Found 2 errors. Watching for file changes."
`;
exports[`multiple serve get initial error and subsequent error 5`] = `"[{\\"frame\\":\\" 1 | import { text } from './text'/n 2 |/n > 3 | var hello1: number = 'Hello1~'/n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^/n 4 | var hello2: boolean = 'Hello2'/n 5 |/n 6 | const rootDom = document.querySelector('#root')!\\",\\"id\\":\\"<PROJECT_ROOT>/temp/multiple/src/main.ts\\",\\"loc\\":{\\"column\\":1,\\"file\\":\\"<PROJECT_ROOT>/temp/multiple/src/main.ts\\",\\"line\\":3},\\"message\\":\\"Unexpected var, use let or const instead.\\",\\"plugin\\":\\"ESLint\\",\\"stack\\":\\"\\"},{\\"frame\\":\\" 2 |/n 3 | var hello1: number = 'Hello1~'/n > 4 | var hello2: boolean = 'Hello2'/n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^/n 5 |/n 6 | const rootDom = document.querySelector('#root')!/n 7 | rootDom.innerHTML = hello1 + text\\",\\"id\\":\\"<PROJECT_ROOT>/temp/multiple/src/main.ts\\",\\"loc\\":{\\"column\\":1,\\"file\\":\\"<PROJECT_ROOT>/temp/multiple/src/main.ts\\",\\"line\\":4},\\"message\\":\\"Unexpected var, use let or const instead.\\",\\"plugin\\":\\"ESLint\\",\\"stack\\":\\"\\"},{\\"frame\\":\\" 1 | import { text } from './text'/n 2 |/n > 3 | var hello1: number = 'Hello1~'/n | ^^^^^^/n 4 | var hello2: boolean = 'Hello2'/n 5 |/n 6 | const rootDom = document.querySelector('#root')!\\",\\"id\\":\\"<PROJECT_ROOT>/temp/multiple/src/main.ts\\",\\"loc\\":{\\"column\\":5,\\"file\\":\\"<PROJECT_ROOT>/temp/multiple/src/main.ts\\",\\"line\\":3},\\"message\\":\\"Type 'string' is not assignable to type 'number'.\\",\\"plugin\\":\\"TypeScript\\",\\"stack\\":\\"\\"},{\\"frame\\":\\" 2 |/n 3 | var hello1: number = 'Hello1~'/n > 4 | var hello2: boolean = 'Hello2'/n | ^^^^^^/n 5 |/n 6 | const rootDom = document.querySelector('#root')!/n 7 | rootDom.innerHTML = hello1 + text\\",\\"id\\":\\"<PROJECT_ROOT>/temp/multiple/src/main.ts\\",\\"loc\\":{\\"column\\":5,\\"file\\":\\"<PROJECT_ROOT>/temp/multiple/src/main.ts\\",\\"line\\":4},\\"message\\":\\"Type 'string' is not assignable to type 'boolean'.\\",\\"plugin\\":\\"TypeScript\\",\\"stack\\":\\"\\"}]"`;
exports[`multiple serve get initial error and subsequent error 6`] = `
" ERROR(ESLint) Unexpected var, use let or const instead.
FILE <PROJECT_ROOT>/temp/multiple/src/main.ts:3:1
1 | import { text } from './text'
2 |
> 3 | var hello1: number = 'Hello1~'
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
4 | var hello2: boolean = 'Hello2'
5 |
6 | const rootDom = document.querySelector('#root')!
ERROR(ESLint) Unexpected var, use let or const instead.
FILE <PROJECT_ROOT>/temp/multiple/src/main.ts:4:1
2 |
3 | var hello1: number = 'Hello1~'
> 4 | var hello2: boolean = 'Hello2'
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
5 |
6 | const rootDom = document.querySelector('#root')!
7 | rootDom.innerHTML = hello1 + text
ERROR(TypeScript) Type 'string' is not assignable to type 'number'.
FILE <PROJECT_ROOT>/temp/multiple/src/main.ts:3:5
1 | import { text } from './text'
2 |
> 3 | var hello1: number = 'Hello1~'
| ^^^^^^
4 | var hello2: boolean = 'Hello2'
5 |
6 | const rootDom = document.querySelector('#root')!
ERROR(TypeScript) Type 'string' is not assignable to type 'boolean'.
FILE <PROJECT_ROOT>/temp/multiple/src/main.ts:4:5
2 |
3 | var hello1: number = 'Hello1~'
> 4 | var hello2: boolean = 'Hello2'
| ^^^^^^
5 |
6 | const rootDom = document.querySelector('#root')!
7 | rootDom.innerHTML = hello1 + text
Found 2 errors. Watching for file changes."
`;

0 comments on commit e1bcfb4

Please sign in to comment.