diff --git a/packages/vite-plugin-checker/__tests__/e2e/Sandbox/Sandbox.ts b/packages/vite-plugin-checker/__tests__/e2e/Sandbox/Sandbox.ts index c7b9ab7b..87b7f6e1 100644 --- a/packages/vite-plugin-checker/__tests__/e2e/Sandbox/Sandbox.ts +++ b/packages/vite-plugin-checker/__tests__/e2e/Sandbox/Sandbox.ts @@ -6,7 +6,7 @@ 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 @@ -14,11 +14,16 @@ 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) + } }, ] } @@ -156,7 +161,7 @@ export async function viteBuild({ cwd = process.cwd(), }: { unexpectedErrorMsg?: string | string[] - expectedErrorMsg?: string + expectedErrorMsg?: string | string[] cwd?: string } = {}) { const promise = execa(binPath, ['build'], { @@ -164,7 +169,12 @@ export async function viteBuild({ }) 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 } diff --git a/packages/vite-plugin-checker/__tests__/e2e/testUtils.ts b/packages/vite-plugin-checker/__tests__/e2e/testUtils.ts index b5f6bc82..a0370152 100644 --- a/packages/vite-plugin-checker/__tests__/e2e/testUtils.ts +++ b/packages/vite-plugin-checker/__tests__/e2e/testUtils.ts @@ -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), diff --git a/packages/vite-plugin-checker/src/@runtime/overlay.ts b/packages/vite-plugin-checker/src/@runtime/overlay.ts index 79083b33..a6c15dea 100644 --- a/packages/vite-plugin-checker/src/@runtime/overlay.ts +++ b/packages/vite-plugin-checker/src/@runtime/overlay.ts @@ -142,8 +142,7 @@ code { ` -const errorItemTemplate = (checkerId: string) => ` -
+const errorItemTemplate = (checkerId: string) => `

     

@@ -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)
diff --git a/packages/vite-plugin-checker/src/FileDiagnosticManager.ts b/packages/vite-plugin-checker/src/FileDiagnosticManager.ts
new file mode 100644
index 00000000..9b3c33e8
--- /dev/null
+++ b/packages/vite-plugin-checker/src/FileDiagnosticManager.ts
@@ -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 }
diff --git a/packages/vite-plugin-checker/src/checkers/eslint/main.ts b/packages/vite-plugin-checker/src/checkers/eslint/main.ts
index 72995750..aa75fd91 100644
--- a/packages/vite-plugin-checker/src/checkers/eslint/main.ts
+++ b/packages/vite-plugin-checker/src/checkers/eslint/main.ts
@@ -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) => {
@@ -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
             //   ? {
@@ -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()
@@ -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
diff --git a/playground/multiple/__tests__/__snapshots__/test.spec.ts.snap b/playground/multiple/__tests__/__snapshots__/test.spec.ts.snap
new file mode 100644
index 00000000..66f9f591
--- /dev/null
+++ b/playground/multiple/__tests__/__snapshots__/test.spec.ts.snap
@@ -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\\":\\"/temp/multiple/src/main.ts\\",\\"loc\\":{\\"column\\":1,\\"file\\":\\"/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\\":\\"/temp/multiple/src/main.ts\\",\\"loc\\":{\\"column\\":1,\\"file\\":\\"/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\\":\\"/temp/multiple/src/main.ts\\",\\"loc\\":{\\"column\\":5,\\"file\\":\\"/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\\":\\"/temp/multiple/src/main.ts\\",\\"loc\\":{\\"column\\":5,\\"file\\":\\"/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  /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  /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  /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  /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\\":\\"/temp/multiple/src/main.ts\\",\\"loc\\":{\\"column\\":1,\\"file\\":\\"/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\\":\\"/temp/multiple/src/main.ts\\",\\"loc\\":{\\"column\\":1,\\"file\\":\\"/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\\":\\"/temp/multiple/src/main.ts\\",\\"loc\\":{\\"column\\":5,\\"file\\":\\"/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\\":\\"/temp/multiple/src/main.ts\\",\\"loc\\":{\\"column\\":5,\\"file\\":\\"/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  /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  /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  /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  /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\\":\\"/temp/multiple/src/main.ts\\",\\"loc\\":{\\"column\\":1,\\"file\\":\\"/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\\":\\"/temp/multiple/src/main.ts\\",\\"loc\\":{\\"column\\":1,\\"file\\":\\"/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\\":\\"/temp/multiple/src/main.ts\\",\\"loc\\":{\\"column\\":5,\\"file\\":\\"/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\\":\\"/temp/multiple/src/main.ts\\",\\"loc\\":{\\"column\\":5,\\"file\\":\\"/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  /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  /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  /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  /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."
+`;
diff --git a/playground/multiple/__tests__/test.spec.ts b/playground/multiple/__tests__/test.spec.ts
new file mode 100644
index 00000000..18b63f50
--- /dev/null
+++ b/playground/multiple/__tests__/test.spec.ts
@@ -0,0 +1,99 @@
+import stringify from 'fast-json-stable-stringify'
+
+import {
+  killServer,
+  preTest,
+  proxyConsoleInTest,
+  resetReceivedLog,
+  sleepForEdit,
+  sleepForServerReady,
+  stripedLog,
+  viteBuild,
+  viteServe,
+} from '../../../packages/vite-plugin-checker/__tests__/e2e/Sandbox/Sandbox'
+import {
+  editFile,
+  sleep,
+  testDir,
+  WORKER_CLEAN_TIMEOUT,
+} from '../../../packages/vite-plugin-checker/__tests__/e2e/testUtils'
+import { WS_CHECKER_ERROR_EVENT } from '../../../packages/vite-plugin-checker/src/client'
+import { copyCode } from '../../../scripts/jestSetupFilesAfterEnv'
+import { serializers } from '../../../scripts/serializers'
+
+beforeAll(async () => {
+  await preTest()
+})
+
+expect.addSnapshotSerializer(serializers)
+
+afterAll(async () => {
+  await sleep(WORKER_CLEAN_TIMEOUT)
+})
+
+describe('multiple', () => {
+  beforeEach(async () => {
+    await copyCode()
+  })
+
+  describe('serve', () => {
+    afterEach(async () => {
+      await killServer()
+    })
+
+    it('get initial error and subsequent error', async () => {
+      let errors: any[] = []
+      await viteServe({
+        cwd: testDir,
+        wsSend: (_payload) => {
+          if (_payload.type === 'custom' && _payload.event === WS_CHECKER_ERROR_EVENT) {
+            errors = errors.concat(_payload.data.errors)
+          }
+        },
+        proxyConsole: () => proxyConsoleInTest(true),
+      })
+      await sleepForServerReady()
+      expect(stringify(errors.sort())).toMatchSnapshot()
+      expect(stripedLog).toMatchSnapshot()
+
+      console.log('-- edit error file --')
+      errors = []
+      resetReceivedLog()
+      editFile('src/main.ts', (code) => code.replace(`'Hello1'`, `'Hello1~'`))
+      await sleepForEdit()
+      expect(stringify(errors.sort())).toMatchSnapshot()
+      expect(stripedLog).toMatchSnapshot()
+
+      console.log('-- edit non error file --')
+      errors = []
+      resetReceivedLog()
+      editFile('src/text.ts', (code) => code.replace(`Multiple`, `multiple`))
+      await sleepForEdit()
+      expect(stringify(errors.sort())).toMatchSnapshot()
+      expect(stripedLog).toMatchSnapshot()
+    })
+  })
+
+  describe('build', () => {
+    const expectedMsg = [
+      '3:1  error  Unexpected var, use let or const instead  no-var',
+      '4:1  error  Unexpected var, use let or const instead  no-var',
+      `src/main.ts(3,5): error TS2322: Type 'string' is not assignable to type 'number'.`,
+      `src/main.ts(4,5): error TS2322: Type 'string' is not assignable to type 'boolean'.`,
+    ]
+
+    it('enableBuild: true', async () => {
+      await viteBuild({ expectedErrorMsg: expectedMsg, cwd: testDir })
+    })
+
+    it('enableBuild: false', async () => {
+      editFile('vite.config.ts', (code) =>
+        code.replace('eslint: {', 'enableBuild: false, eslint: {')
+      )
+      await viteBuild({
+        unexpectedErrorMsg: 'error',
+        cwd: testDir,
+      })
+    })
+  })
+})
diff --git a/playground/multiple/index.html b/playground/multiple/index.html
new file mode 100644
index 00000000..5f4067d6
--- /dev/null
+++ b/playground/multiple/index.html
@@ -0,0 +1,13 @@
+
+
+  
+    
+    
+    
+    Vite App
+  
+  
+    
+ + + diff --git a/playground/multiple/package.json b/playground/multiple/package.json new file mode 100644 index 00000000..6c951a38 --- /dev/null +++ b/playground/multiple/package.json @@ -0,0 +1,23 @@ +{ + "private": true, + "name": "vite-react-typescript-starter", + "version": "0.0.0", + "scripts": { + "dev": "vite", + "build": "vite build", + "serve": "vite preview", + "lint": "eslint --ext .js,.ts ./src/**" + }, + "dependencies": { + "react": "^17.0.0", + "react-dom": "^17.0.0" + }, + "devDependencies": { + "@types/react": "^17.0.0", + "@types/react-dom": "^17.0.0", + "@vitejs/plugin-react-refresh": "^1.3.1", + "typescript": "^4.1.2", + "vite": "^2.7.13", + "vite-plugin-checker": "workspace:*" + } +} diff --git a/playground/multiple/src/main.ts b/playground/multiple/src/main.ts new file mode 100644 index 00000000..0a8e5eb5 --- /dev/null +++ b/playground/multiple/src/main.ts @@ -0,0 +1,9 @@ +import { text } from './text' + +var hello1: number = 'Hello1' +var hello2: boolean = 'Hello2' + +const rootDom = document.querySelector('#root')! +rootDom.innerHTML = hello1 + text + +export {} diff --git a/playground/multiple/src/text.ts b/playground/multiple/src/text.ts new file mode 100644 index 00000000..050791ca --- /dev/null +++ b/playground/multiple/src/text.ts @@ -0,0 +1 @@ +export const text = 'Multiple Checkers' diff --git a/playground/multiple/tsconfig.json b/playground/multiple/tsconfig.json new file mode 100644 index 00000000..36f84d9e --- /dev/null +++ b/playground/multiple/tsconfig.json @@ -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"] +} diff --git a/playground/multiple/vite.config.ts b/playground/multiple/vite.config.ts new file mode 100644 index 00000000..5d951b21 --- /dev/null +++ b/playground/multiple/vite.config.ts @@ -0,0 +1,16 @@ +import { defineConfig } from 'vite' +import reactRefresh from '@vitejs/plugin-react-refresh' +import checker from 'vite-plugin-checker' + +// https://vitejs.dev/config/ +export default defineConfig({ + plugins: [ + reactRefresh(), + checker({ + typescript: true, + eslint: { + lintCommand: 'eslint ./src --ext .ts', + }, + }), + ], +}) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e2ac3d85..ff63ebc8 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -127,6 +127,27 @@ importers: optionator: 0.9.1 vls: 0.7.4 + playground/multiple: + specifiers: + '@types/react': ^17.0.0 + '@types/react-dom': ^17.0.0 + '@vitejs/plugin-react-refresh': ^1.3.1 + react: ^17.0.0 + react-dom: ^17.0.0 + typescript: ^4.1.2 + vite: ^2.7.13 + vite-plugin-checker: workspace:* + dependencies: + react: 17.0.2 + react-dom: 17.0.2_react@17.0.2 + devDependencies: + '@types/react': 17.0.11 + '@types/react-dom': 17.0.7 + '@vitejs/plugin-react-refresh': 1.3.3 + typescript: 4.3.2 + vite: 2.7.13 + vite-plugin-checker: link:../../packages/vite-plugin-checker + playground/react-ts: specifiers: '@types/react': ^17.0.0