├── .gitattributes ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.yaml │ └── feature_request.yaml ├── renovate.json5 └── workflows │ ├── ci.yml │ ├── release.yml │ └── sync-volar.yml ├── .gitignore ├── .npmrc ├── .nvmrc ├── .vscode ├── extensions.json └── settings.json ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── biome.json ├── docs ├── .vitepress │ └── config.ts ├── checkers │ ├── biome.md │ ├── eslint.md │ ├── overview.md │ ├── stylelint.md │ ├── typescript.md │ ├── vls.md │ └── vue-tsc.md ├── configuration │ └── config.md ├── faq │ └── integration.md ├── index.md ├── introduction.md ├── introduction │ ├── getting-started.md │ └── introduction.md ├── package.json └── public │ └── logo.png ├── knip.json ├── netlify.toml ├── package.json ├── packages ├── runtime │ ├── .gitignore │ ├── README.md │ ├── index.html │ ├── package.json │ ├── src │ │ ├── App.ce.vue │ │ ├── components │ │ │ ├── Badge.ce.vue │ │ │ ├── Checker.ce.vue │ │ │ ├── Diagnostic.ce.vue │ │ │ └── List.ce.vue │ │ ├── main.ts │ │ ├── useChecker.ts │ │ ├── vite-env.d.ts │ │ └── ws.ts │ ├── tsconfig.json │ ├── tsconfig.node.json │ └── vite.config.ts └── vite-plugin-checker │ ├── CHANGELOG.md │ ├── __tests__ │ ├── __snapshots__ │ │ ├── logger.spec.ts.snap │ │ └── vlsConfig.spec.ts.snap │ ├── codeFrame.spec.ts │ ├── fixtures │ │ ├── eslintDiagnostic.ts │ │ └── tsDiagnostic.ts │ ├── logger.spec.ts │ └── vlsConfig.spec.ts │ ├── package.json │ ├── src │ ├── Checker.ts │ ├── FileDiagnosticManager.ts │ ├── checkers │ │ ├── biome │ │ │ ├── cli.ts │ │ │ ├── main.ts │ │ │ └── types.ts │ │ ├── eslint │ │ │ ├── cli.ts │ │ │ ├── main.ts │ │ │ └── options.ts │ │ ├── stylelint │ │ │ ├── argv.ts │ │ │ ├── main.ts │ │ │ └── options.ts │ │ ├── typescript │ │ │ └── main.ts │ │ ├── vls │ │ │ ├── diagnostics.ts │ │ │ ├── initParams.ts │ │ │ ├── main.ts │ │ │ └── typings.d.ts │ │ └── vueTsc │ │ │ ├── languagePlugins.cjs │ │ │ ├── main.ts │ │ │ └── prepareVueTsc.ts │ ├── client │ │ └── index.ts │ ├── codeFrame.ts │ ├── glob.ts │ ├── logger.ts │ ├── main.ts │ ├── types.ts │ ├── utils.ts │ └── worker.ts │ ├── tsconfig.json │ └── tsup.config.ts ├── playground ├── backend-integration │ ├── .gitignore │ ├── __test__ │ │ ├── __snapshots__ │ │ │ └── test.spec.ts.snap │ │ ├── serve.js │ │ └── test.spec.ts │ ├── index.html │ ├── package.json │ ├── public │ │ └── vite.svg │ ├── server.js │ ├── src │ │ ├── App.css │ │ ├── App.tsx │ │ ├── assets │ │ │ └── react.svg │ │ ├── index.css │ │ ├── main.tsx │ │ └── vite-env.d.ts │ ├── tsconfig.json │ ├── tsconfig.node.json │ └── vite.config.js ├── biome-default │ ├── __tests__ │ │ ├── __snapshots__ │ │ │ └── test.spec.ts.snap │ │ └── test.spec.ts │ ├── biome.json │ ├── index.html │ ├── package.json │ ├── src │ │ └── index.js │ ├── tsconfig.json │ └── vite.config.js ├── config-default │ ├── .eslintrc.json │ ├── __tests__ │ │ ├── __snapshots__ │ │ │ └── test.spec.ts.snap │ │ └── test.spec.ts │ ├── index.html │ ├── package.json │ ├── src │ │ ├── main.ts │ │ └── text.ts │ ├── tsconfig.json │ └── vite.config.js ├── config-enableBuild-false │ ├── .eslintrc.json │ ├── __tests__ │ │ └── test.spec.ts │ ├── index.html │ ├── package.json │ ├── src │ │ ├── main.ts │ │ └── text.ts │ ├── tsconfig.json │ └── vite.config.js ├── config-initialIsOpen-error-warnings │ ├── .eslintrc.json │ ├── __tests__ │ │ ├── __snapshots__ │ │ │ └── test.spec.ts.snap │ │ └── test.spec.ts │ ├── index.html │ ├── package.json │ ├── src │ │ ├── main.ts │ │ └── text.ts │ ├── tsconfig.json │ └── vite.config.js ├── config-initialIsOpen-error │ ├── .eslintrc.json │ ├── __tests__ │ │ ├── __snapshots__ │ │ │ └── test.spec.ts.snap │ │ └── test.spec.ts │ ├── index.html │ ├── package.json │ ├── src │ │ ├── main.ts │ │ └── text.ts │ ├── tsconfig.json │ └── vite.config.js ├── config-initialIsOpen-false │ ├── .eslintrc.json │ ├── __tests__ │ │ └── test.spec.ts │ ├── index.html │ ├── package.json │ ├── src │ │ ├── main.ts │ │ └── text.ts │ ├── tsconfig.json │ └── vite.config.js ├── config-no-runtime-in-build │ ├── .eslintrc.json │ ├── __tests__ │ │ └── test.spec.ts │ ├── index.html │ ├── package.json │ ├── src │ │ ├── main.ts │ │ └── text.ts │ ├── tsconfig.json │ └── vite.config.js ├── config-overlay-changes │ ├── .eslintrc.json │ ├── __tests__ │ │ ├── __snapshots__ │ │ │ └── test.spec.ts.snap │ │ └── test.spec.ts │ ├── index.html │ ├── package.json │ ├── src │ │ ├── main.ts │ │ └── text.ts │ ├── tsconfig.json │ └── vite.config.js ├── config-overlay-false │ ├── .eslintrc.json │ ├── __tests__ │ │ └── test.spec.ts │ ├── index.html │ ├── package.json │ ├── src │ │ ├── main.ts │ │ └── text.ts │ ├── tsconfig.json │ └── vite.config.js ├── config-overlay-position-style │ ├── .eslintrc.json │ ├── __tests__ │ │ └── test.spec.ts │ ├── index.html │ ├── package.json │ ├── src │ │ ├── main.ts │ │ └── text.ts │ ├── tsconfig.json │ └── vite.config.js ├── config-terminal-false │ ├── .eslintrc.json │ ├── __tests__ │ │ ├── __snapshots__ │ │ │ └── test.spec.ts.snap │ │ └── test.spec.ts │ ├── index.html │ ├── package.json │ ├── src │ │ ├── main.ts │ │ └── text.ts │ ├── tsconfig.json │ └── vite.config.js ├── eslint-config-log-level │ ├── .eslintrc.json │ ├── __tests__ │ │ ├── __snapshots__ │ │ │ └── test.spec.ts.snap │ │ └── test.spec.ts │ ├── index.html │ ├── package.json │ ├── src │ │ ├── main.ts │ │ └── text.ts │ ├── tsconfig.json │ └── vite.config.js ├── eslint-default │ ├── .eslintrc.json │ ├── __tests__ │ │ ├── __snapshots__ │ │ │ └── test.spec.ts.snap │ │ └── test.spec.ts │ ├── index.html │ ├── package.json │ ├── src │ │ ├── main.ts │ │ └── text.ts │ ├── tsconfig.json │ └── vite.config.js ├── eslint-flat │ ├── __tests__ │ │ ├── __snapshots__ │ │ │ └── test.spec.ts.snap │ │ └── test.spec.ts │ ├── eslint.config.js │ ├── index.html │ ├── package.json │ ├── src │ │ └── index.js │ ├── tsconfig.json │ └── vite.config.js ├── multiple-hmr │ ├── .eslintrc.json │ ├── __tests__ │ │ ├── __snapshots__ │ │ │ └── test.spec.ts.snap │ │ └── test.spec.ts │ ├── index.html │ ├── package.json │ ├── src │ │ ├── App.css │ │ ├── App.tsx │ │ ├── favicon.svg │ │ ├── index.css │ │ ├── logo.svg │ │ ├── main.tsx │ │ └── value.ts │ ├── tsconfig.json │ └── vite.config.js ├── multiple-reload │ ├── .eslintrc.json │ ├── __tests__ │ │ ├── __snapshots__ │ │ │ └── test.spec.ts.snap │ │ └── test.spec.ts │ ├── index.html │ ├── package.json │ ├── src │ │ ├── main.ts │ │ └── text.ts │ ├── tsconfig.json │ └── vite.config.js ├── serializers.ts ├── stylelint-default │ ├── .eslintrc.json │ ├── .stylelintrc.json │ ├── __tests__ │ │ ├── __snapshots__ │ │ │ └── test.spec.ts.snap │ │ └── test.spec.ts │ ├── index.html │ ├── package.json │ ├── src │ │ ├── main.ts │ │ └── style.css │ ├── tsconfig.json │ └── vite.config.js ├── testUtils.ts ├── tsconfig.json ├── typescript-react │ ├── __tests__ │ │ ├── __snapshots__ │ │ │ └── test.spec.ts.snap │ │ └── test.spec.ts │ ├── index.html │ ├── package.json │ ├── src │ │ ├── App.css │ │ ├── App.tsx │ │ ├── favicon.svg │ │ ├── index.css │ │ ├── logo.svg │ │ └── main.tsx │ ├── tsconfig.json │ └── vite.config.js ├── vitestGlobalSetup.ts ├── vitestSetup.ts ├── vls-vue2 │ ├── .browserslistrc │ ├── .eslintrc.js │ ├── .gitignore │ ├── .npmrc │ ├── LICENSE │ ├── README.md │ ├── __tests__ │ │ ├── __snapshots__ │ │ │ └── test.spec.ts.snap │ │ └── test.spec.ts │ ├── index.html │ ├── package.json │ ├── public │ │ └── favicon.ico │ ├── src │ │ ├── App.vue │ │ ├── assets │ │ │ └── logo.png │ │ ├── components │ │ │ └── HelloWorld.vue │ │ ├── main.ts │ │ ├── router │ │ │ └── index.ts │ │ ├── shims-tsx.d.ts │ │ ├── shims-vue.d.ts │ │ ├── store │ │ │ └── index.ts │ │ └── views │ │ │ ├── About.vue │ │ │ └── Home.vue │ ├── tsconfig.json │ ├── vetur.config.cjs │ └── vite.config.js └── vue-tsc-vue3 │ ├── __tests__ │ ├── __snapshots__ │ │ └── test.spec.ts.snap │ └── test.spec.ts │ ├── index.html │ ├── package.json │ ├── public │ └── favicon.ico │ ├── src │ ├── App.vue │ ├── assets │ │ └── logo.png │ ├── components │ │ └── HelloWorld.vue │ ├── main.ts │ └── shims-vue.d.ts │ ├── tsconfig.json │ └── vite.config.js ├── pnpm-lock.yaml ├── pnpm-workspace.yaml ├── scripts ├── docs-check.sh └── vitestGlobalSetup.ts ├── vitest.config.e2e.ts └── vitest.config.ts /.gitattributes: -------------------------------------------------------------------------------- 1 | # https://stackoverflow.com/questions/61958231/github-actions-prettier-finds-errors-only-on-windows-latest 2 | * text=auto eol=lf -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.yaml: -------------------------------------------------------------------------------- 1 | name: "\U0001F680 New feature proposal" 2 | description: Suggest an idea for this project 3 | body: 4 | - type: markdown 5 | attributes: 6 | value: | 7 | Thanks for your interest in the project and taking the time to fill out this feature report! 8 | - type: textarea 9 | id: feature-description 10 | attributes: 11 | label: Is your feature request related to a problem? Please describe. 12 | description: "A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]" 13 | validations: 14 | required: true 15 | - type: textarea 16 | id: suggested-solution 17 | attributes: 18 | label: Describe the solution you'd like 19 | description: A clear and concise description of what you want to happen. 20 | validations: 21 | required: true 22 | - type: textarea 23 | id: alternative 24 | attributes: 25 | label: Describe alternatives you've considered 26 | description: A clear and concise description of any alternative solutions or features you've considered. 27 | - type: textarea 28 | id: additional-context 29 | attributes: 30 | label: Additional context 31 | description: Add any other context or screenshots about the feature request here. 32 | - type: checkboxes 33 | id: checkboxes 34 | attributes: 35 | label: Validations 36 | description: Before submitting the issue, please make sure you do the following 37 | options: 38 | - label: Read the [docs](https://github.com/fi3ework/vite-plugin-checker#readme). 39 | required: true 40 | - label: Read the [Contributing Guidelines](https://github.com/fi3ework/vite-plugin-checker/blob/main/CONTRIBUTING.md). 41 | required: true 42 | - label: Check that there isn't already an issue that asks for the same feature to avoid creating a duplicate. 43 | required: true 44 | -------------------------------------------------------------------------------- /.github/renovate.json5: -------------------------------------------------------------------------------- 1 | { 2 | $schema: 'https://docs.renovatebot.com/renovate-schema.json', 3 | extends: ['config:recommended', 'schedule:weekly'], 4 | ignorePaths: ['**/tests/**', '**/node_modules/**'], 5 | labels: ['dependencies'], 6 | rangeStrategy: 'bump', 7 | ignoreDeps: [ 8 | // manually update some packages 9 | 'pnpm', 10 | 'typescript', 11 | // align Node.js version minimum requirements 12 | '@types/node', 13 | 'node', 14 | ], 15 | } 16 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | permissions: 4 | contents: write 5 | 6 | on: workflow_dispatch 7 | 8 | jobs: 9 | release: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v4 13 | with: 14 | fetch-depth: 0 15 | 16 | - name: Install pnpm 17 | uses: pnpm/action-setup@v4 18 | 19 | - name: Set node 20 | uses: actions/setup-node@v4 21 | with: 22 | node-version: 18 23 | cache: pnpm 24 | registry-url: 'https://registry.npmjs.org' 25 | 26 | - name: Setup npmrc 27 | # https://github.com/changesets/action/issues/98#issuecomment-917292485 28 | run: echo "//registry.npmjs.org/:_authToken=${{ secrets.NPM_TOKEN }}" >> .npmrc 29 | 30 | - name: Install dependencies 31 | run: pnpm i 32 | 33 | - name: Prepare for release 34 | run: | 35 | pnpm run format 36 | pnpm run lint 37 | pnpm run type-check 38 | pnpm run clean 39 | pnpm run build 40 | pnpm run publint 41 | 42 | - uses: actions/github-script@v7 43 | id: version_to_release 44 | with: 45 | result-encoding: string 46 | script: | 47 | const fs = require('fs'); 48 | const packageJson = JSON.parse(fs.readFileSync('./packages/vite-plugin-checker/package.json', 'utf8')); 49 | return packageJson.version; 50 | 51 | - name: Publish to NPM 52 | run: | 53 | cp README.md packages/vite-plugin-checker/README.md 54 | git tag vite-plugin-checker@${{ steps.version_to_release.outputs.result }} 55 | git push origin vite-plugin-checker@${{ steps.version_to_release.outputs.result }} 56 | pnpm -r publish --no-git-checks 57 | 58 | - name: GitHub release 59 | run: pnpx changelogithub 60 | env: 61 | GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} 62 | -------------------------------------------------------------------------------- /.github/workflows/sync-volar.yml: -------------------------------------------------------------------------------- 1 | name: Create issue when sync up Volar is required 2 | 3 | on: 4 | schedule: 5 | - cron: '0 0 * * *' 6 | workflow_dispatch: 7 | 8 | jobs: 9 | check_file_changes: 10 | runs-on: ubuntu-latest 11 | 12 | steps: 13 | - name: Checkout repository 14 | uses: actions/checkout@v4 15 | with: 16 | repository: 'volarjs/volar.js' 17 | fetch-depth: 0 18 | 19 | - name: Check if runTsc.ts changed in last 24 hours 20 | id: check_changes 21 | run: | 22 | CHANGED=$(git log --name-only --since="24 hours ago" --pretty=format: | sort | uniq | grep -q "packages/typescript/lib/quickstart/runTsc.ts" && echo "true" || echo "false") 23 | echo "file_changed=${CHANGED}" 24 | echo "file_changed=${CHANGED}" >> $GITHUB_OUTPUT 25 | 26 | - name: Create issue if runTsc.ts changed 27 | if: steps.check_changes.outputs.file_changed == 'true' 28 | uses: actions/github-script@v7 29 | with: 30 | github-token: ${{ secrets.GITHUB_TOKEN }} 31 | script: | 32 | const { owner, repo } = context.repo; 33 | 34 | const date = new Date(); 35 | const options = { year: 'numeric', month: '2-digit', day: '2-digit' }; 36 | const formattedDate = date.toLocaleDateString('en-CA', options); 37 | 38 | const issueTitle = `Volar Change Detected in Last 24 Hours (${formattedDate})`; 39 | # write new line for each line in issueBody 40 | const issueBody = "## ✨ Sync up needed\n\nThe file [`packages/typescript/lib/quickstart/runTsc.ts`](https://github.com/volarjs/volar.js/blob/master/packages/typescript/lib/quickstart/runTsc.ts) in Volar has changed in the last 24 hours.\n\nFile [`packages/vite-plugin-checker/src/checkers/vueTsc/prepareVueTsc.ts`](https://github.com/fi3ework/vite-plugin-checker/blob/main/packages/vite-plugin-checker/src/checkers/vueTsc/prepareVueTsc.ts) contains some code copied from there, please help to sync up the modification from Volar.\n\nRemember to close this issue when is sync up is finished."; 41 | 42 | await github.rest.issues.create({ 43 | owner, 44 | repo, 45 | title: issueTitle, 46 | body: issueBody, 47 | labels: ['vue-tsc'] 48 | }); 49 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | 9 | # Diagnostic reports (https://nodejs.org/api/report.html) 10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | *.lcov 24 | 25 | # nyc test coverage 26 | .nyc_output 27 | 28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 29 | .grunt 30 | 31 | # Bower dependency directory (https://bower.io/) 32 | bower_components 33 | 34 | # node-waf configuration 35 | .lock-wscript 36 | 37 | # Compiled binary addons (https://nodejs.org/api/addons.html) 38 | build/Release 39 | 40 | # Dependency directories 41 | node_modules/ 42 | jspm_packages/ 43 | 44 | # TypeScript v1 declaration files 45 | typings/ 46 | 47 | # TypeScript cache 48 | *.tsbuildinfo 49 | 50 | # Optional npm cache directory 51 | .npm 52 | 53 | # Optional eslint cache 54 | .eslintcache 55 | 56 | # Microbundle cache 57 | .rpt2_cache/ 58 | .rts2_cache_cjs/ 59 | .rts2_cache_es/ 60 | .rts2_cache_umd/ 61 | 62 | # Optional REPL history 63 | .node_repl_history 64 | 65 | # Output of 'npm pack' 66 | *.tgz 67 | 68 | # Yarn Integrity file 69 | .yarn-integrity 70 | 71 | # dotenv environment variables file 72 | .env 73 | .env.test 74 | 75 | # parcel-bundler cache (https://parceljs.org/) 76 | .cache 77 | 78 | # Next.js build output 79 | .next 80 | 81 | # Nuxt.js build / generate output 82 | .nuxt 83 | dist 84 | 85 | # Gatsby files 86 | .cache/ 87 | # Comment in the public line in if your project uses Gatsby and *not* Next.js 88 | # https://nextjs.org/blog/next-9-1#public-directory-support 89 | # public 90 | 91 | # vuepress build output 92 | .vuepress/dist 93 | 94 | # Serverless directories 95 | .serverless/ 96 | 97 | # FuseBox cache 98 | .fusebox/ 99 | 100 | # DynamoDB Local files 101 | .dynamodb/ 102 | 103 | # TernJS port file 104 | .tern-port 105 | 106 | # 107 | lib/ 108 | playground-temp/ 109 | packages/runtime/public/build/bundle.js 110 | docs/.vitepress/cache -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | registry=https://registry.npmjs.org/ 2 | auto-install-peers=false 3 | hoist=false 4 | shell-emulator=true 5 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | v23 2 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "vue.volar", 4 | "vue.vscode-typescript-vue-plugin", 5 | "biomejs.biome" 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "vetur.experimental.templateInterpolationService": true, 3 | "typescript.tsdk": "node_modules/typescript/lib", 4 | "vitest.disableWorkspaceWarning": true, 5 | "cSpell.words": ["Stylelint"], 6 | "editor.codeActionsOnSave": { 7 | "source.organizeImports.biome": "always" 8 | }, 9 | "[javascript]": { 10 | "editor.defaultFormatter": "biomejs.biome" 11 | }, 12 | "[javascriptreact]": { 13 | "editor.defaultFormatter": "biomejs.biome" 14 | }, 15 | "[json5]": { 16 | "editor.defaultFormatter": "biomejs.biome" 17 | }, 18 | "[json]": { 19 | "editor.defaultFormatter": "biomejs.biome" 20 | }, 21 | "[jsonc]": { 22 | "editor.defaultFormatter": "biomejs.biome" 23 | }, 24 | "[typescript]": { 25 | "editor.defaultFormatter": "biomejs.biome" 26 | }, 27 | "[typescriptreact]": { 28 | "editor.defaultFormatter": "biomejs.biome" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # CONTRIBUTING 2 | 3 | ## Development 4 | 5 | ```bash 6 | # Make sure that corepack(https://github.com/nodejs/corepack) is enabled. 7 | corepack enable 8 | pnpm i 9 | pnpm build 10 | pnpm dev 11 | ``` 12 | 13 | ## Publish 14 | 15 | Package is automated published in CI, see `.github/workflows/release.yml`. 16 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 fi3ework 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 | # Visit [documentation](https://vite-plugin-checker.netlify.app) for usage 2 | 3 | A Vite plugin that can run TypeScript, VLS, vue-tsc, ESLint, Biome, Stylelint in worker thread. 4 | 5 |

6 | screenshot 7 |

8 | 9 | ## Online playground 10 | 11 | | Examples | StackBlitz | 12 | | ------------------ | ---------------------------------------------------------------- | 13 | | Vue3 + vue-tsc | [⚡️ StackBlitz](https://stackblitz.com/edit/vitejs-vite-e8pddl) | 14 | | React + TypeScript | [⚡️ StackBlitz](https://stackblitz.com/edit/vitejs-vite-b4zcev) | 15 | | ESLint | [⚡️ StackBlitz](https://stackblitz.com/edit/vitejs-vite-l1ritu) | 16 | | Vue2 + VLS | [⚡️ StackBlitz](https://stackblitz.com/edit/vitejs-vite-kpffk5) | 17 | | Multiple | [⚡️ StackBlitz](https://stackblitz.com/edit/vitejs-vite-mb4ea6) | 18 | 19 | ## License 20 | 21 | MIT License © 2022 [fi3ework](https://github.com/fi3ework) 22 | -------------------------------------------------------------------------------- /biome.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://biomejs.dev/schemas/1.8.3/schema.json", 3 | "organizeImports": { 4 | "include": [ 5 | "./**/*.js", 6 | "./**/*.jsx", 7 | "./**/*.ts", 8 | "./**/*.tsx", 9 | "./**/*.mjs", 10 | "./**/*.cjs" 11 | ] 12 | }, 13 | "vcs": { 14 | "enabled": true, 15 | "defaultBranch": "main", 16 | "clientKind": "git", 17 | "useIgnoreFile": true 18 | }, 19 | "files": { 20 | "ignore": [ 21 | "*.vue", 22 | "playground", 23 | "packages/vite-plugin-checker/src/checkers/eslint/options.ts", 24 | "packages/vite-plugin-checker/src/checkers/stylelint/options.ts", 25 | "playground/*/src/**/*.*" 26 | ], 27 | "ignoreUnknown": true 28 | }, 29 | "formatter": { 30 | "indentStyle": "space" 31 | }, 32 | "javascript": { 33 | "formatter": { 34 | "semicolons": "asNeeded", 35 | "quoteStyle": "single" 36 | } 37 | }, 38 | "linter": { 39 | "enabled": true, 40 | "rules": { 41 | "recommended": true, 42 | "style": { 43 | "noNonNullAssertion": "off", 44 | "useFilenamingConvention": { 45 | "level": "error", 46 | "options": { 47 | "filenameCases": ["camelCase", "PascalCase", "export"] 48 | } 49 | } 50 | }, 51 | "suspicious": { 52 | "noExplicitAny": "off", 53 | "noConfusingVoidType": "off" 54 | }, 55 | "performance": { 56 | "noDelete": "off" 57 | } 58 | } 59 | }, 60 | "overrides": [ 61 | { 62 | "include": ["packages/runtime/src/vite-env.d.ts"], 63 | "linter": { 64 | "rules": { 65 | "style": { 66 | "useFilenamingConvention": "off" 67 | } 68 | } 69 | } 70 | } 71 | ] 72 | } 73 | -------------------------------------------------------------------------------- /docs/.vitepress/config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vitepress' 2 | 3 | export default defineConfig({ 4 | lang: 'en-US', 5 | title: 'vite-plugin-checker', 6 | description: 7 | 'Vite plugin that provide checks of TypeScript, ESLint, Biome, vue-tsc, and more.', 8 | lastUpdated: true, 9 | themeConfig: { 10 | outline: 'deep', 11 | sidebar: { 12 | '/': sidebar(), 13 | }, 14 | editLink: { 15 | pattern: 16 | 'https://github.com/fi3ework/vite-plugin-checker/edit/main/docs/:path', 17 | text: 'Edit this page on GitHub', 18 | }, 19 | socialLinks: [ 20 | { 21 | icon: 'github', 22 | link: 'https://github.com/fi3ework/vite-plugin-checker', 23 | }, 24 | { 25 | icon: 'npm', 26 | link: 'https://www.npmjs.com/package/vite-plugin-checker', 27 | }, 28 | ], 29 | footer: { 30 | message: 'Released under the MIT License.', 31 | copyright: 'Copyright fi3ework', 32 | }, 33 | }, 34 | }) 35 | 36 | function sidebar() { 37 | return [ 38 | { 39 | text: 'Introduction', 40 | collapsible: true, 41 | items: [ 42 | { text: 'Introduction', link: '/introduction/introduction' }, 43 | { text: 'Getting Started', link: '/introduction/getting-started' }, 44 | ], 45 | }, 46 | { 47 | text: 'Checkers', 48 | collapsible: true, 49 | items: [ 50 | { text: 'Overview', link: '/checkers/overview' }, 51 | { text: 'TypeScript', link: '/checkers/typescript' }, 52 | { text: 'vue-tsc', link: '/checkers/vue-tsc' }, 53 | { text: 'ESLint', link: '/checkers/eslint' }, 54 | { text: 'Biome', link: '/checkers/biome' }, 55 | { text: 'Stylelint', link: '/checkers/stylelint' }, 56 | { text: 'VLS', link: '/checkers/vls' }, 57 | ], 58 | }, 59 | { 60 | text: 'Configuration', 61 | collapsible: true, 62 | items: [{ text: 'Shared ', link: '/configuration/config' }], 63 | }, 64 | { 65 | text: 'FAQs', 66 | collapsible: true, 67 | items: [{ text: 'Integration', link: '/faq/integration' }], 68 | }, 69 | ] 70 | } 71 | -------------------------------------------------------------------------------- /docs/checkers/overview.md: -------------------------------------------------------------------------------- 1 | # Checkers overview 2 | 3 | vite-plugin-checkers provide built-in checkers. For now, it supports [TypeScript](/checkers/typescript), [ESLint](/checkers/eslint), [Biome](/checkers/biome), [vue-tsc](/checkers/vue-tsc), [VLS](/checkers/vls), [Stylelint](/checkers/stylelint). 4 | 5 | ## How to add a checker 6 | 7 | - Set the checker property to `true` to use a checker with its default value (except ESLint and Stylelint). 8 | - Leave the field blank or `false` will not use the checker. 9 | - Make sure to install the peer dependencies that checker relies on (documented on each checker's page if needed). 10 | - Checker can be enabled with an advanced object config. 11 | - Use [config](/configuration/config) to control the common behaviors settings of checkers. 12 | -------------------------------------------------------------------------------- /docs/checkers/typescript.md: -------------------------------------------------------------------------------- 1 | # TypeScript 2 | 3 | You can use TypeScript checker for vanilla TypeScript project or React project. 4 | 5 | ## Installation 6 | 7 | 1. Make sure [typescript](https://www.npmjs.com/package/typescript) is installed as a peer dependency. 8 | 9 | 2. Add `typescript` field to plugin config. 10 | 11 | ```js 12 | export default { 13 | plugins: [checker({ typescript: true /** or an object config */ })], 14 | } 15 | ``` 16 | 17 | ## Configuration 18 | 19 | Advanced object configuration table of `options.typescript`. 20 | 21 | | field | Type | Default value | Description | 22 | | :----------- | --------- | ----------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | 23 | | root | `string` | [Vite config](https://vitejs.dev/config/#root) `root` | Root path to find tsconfig file | 24 | | tsconfigPath | `string` | `"tsconfig.json"` | Relative tsconfig path to `root` | 25 | | buildMode | `boolean` | `false` | Add [`--build`](https://www.typescriptlang.org/docs/handbook/project-references.html) to `tsc` flag, note that `noEmit` does NOT work if `buildMode` is `true` ([#36917](https://github.com/microsoft/TypeScript/issues/36917)) | 26 | -------------------------------------------------------------------------------- /docs/checkers/vls.md: -------------------------------------------------------------------------------- 1 | # VLS 2 | 3 | You can use VLS checker for your Vue2 project. If you're using Vue3, choose [vue-tsc](/checkers/vue-tsc) checker. 4 | 5 | ## Installation 6 | 7 | 1. Make sure [vls](https://www.npmjs.com/package/vls) and [vti](https://www.npmjs.com/package/vti) are installed as peer dependencies, plugin will use vls on dev mode and vti on build mode. 8 | 9 | ```bash 10 | pnpm add vls vti -D 11 | ``` 12 | 13 | 2. Add `vls` field to plugin config. 14 | 15 | ```js 16 | module.exports = { 17 | plugins: [checker({ vls: true })], 18 | } 19 | ``` 20 | 21 | ## Configuration 22 | 23 | Advanced object configuration of `options.vls` 24 | 25 | VLS configuration accepts the same values that can be configured in VS code with keys that start with `vetur`. 26 | These are configured with nested objects rather than dotted string notation. TypeScript intellisense is available. 27 | 28 | See [`initParams.ts`](https://github.com/fi3ework/vite-plugin-checker/blob/8fc5d7f4a908a4c80d1cb978e0acf1d4e5700e6a/packages/vite-plugin-checker/src/checkers/vls/initParams.ts#L33) for a comprehensive list of the defaults that can be overridden. Unfortunately, Vetur does not provide a single comprehensive document of all its options. 29 | 30 | For example, to performing checking only the ` 12 | 13 | 14 | 15 | ``` 16 | 17 | ## with Nuxt3 18 | 19 | ### overlay does not display in development mode 20 | 21 | There're two ways to use vite-plugin-checker with Nuxt3 for now. 22 | 23 | #### Use vite-plugin-checker as a normal Vite plugin (require version >= 0.5.5) 24 | 25 | There are a few steps to do: 26 | 27 | 1. Add `vite-plugin-checker` `typescript` `vue-tsc` `@types/node` as devDependencies to your Nuxt project. 28 | 2. Create a Vue component with content: 29 | ```vue 30 | // vite-plugin-checker.vue 31 | 34 | ``` 35 | 3. Add vite-plugin-checker to `vite.plugins` in `nuxt.config.ts`. 36 | 37 | ```ts 38 | import { checker } from 'vite-plugin-checker' 39 | // https://nuxt.com/docs/api/configuration/nuxt-config 40 | export default defineNuxtConfig({ 41 | vite: { 42 | plugins: [ 43 | checker({ 44 | vueTsc: true, 45 | }), 46 | ], 47 | }, 48 | }) 49 | ``` 50 | 51 | 4. Import component above in the root component of your Nuxt project to have a global error overlay. 52 | 53 | ```vue 54 | 57 | 58 | 66 | ``` 67 | 68 | You are all set in both development and build mode. 69 | 70 | #### Enable vite-plugin-checker as a built-in Nuxt functionality 71 | 72 | ::: warning 73 | The error overlay can not be displayed in this way, we'll try to fix this with Nuxt in the future. 74 | ::: 75 | 76 | See Nuxt's official documentation's [typecheck section](https://nuxt.com/docs/api/commands/typecheck#nuxi-typecheck). 77 | -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: home 3 | 4 | title: vite-plugin-checker 5 | titleTemplate: Vite plugin that provide checks of TypeScript, ESLint, vue-tsc, and more. 6 | 7 | hero: 8 | name: vite-plugin-checker 9 | text: Add Checks to Your Vite Project 10 | tagline: Vite plugin that provide checks of TypeScript, ESLint, vue-tsc, and more. 11 | image: 12 | src: /logo.png 13 | alt: vite-plugin-checker 14 | actions: 15 | - theme: brand 16 | text: Get Started 17 | link: /introduction/introduction 18 | - theme: alt 19 | text: View on GitHub 20 | link: https://github.com/fi3ework/vite-plugin-checker 21 | 22 | features: 23 | - title: Mainstream checks provided 24 | details: Speeds up TypeScript, vue-tsc, ESLint, etc. checks by running in a worker thread in serve mode 25 | - title: Integration with frameworks 26 | details: Works good with vanilla JS / TS, React, Vue2, Vue3 27 | - title: Variety of ways to prompt 28 | details: Prompt errors in an overlay UI and terminal 29 | - title: In all modes 30 | details: Works both in Vite serve and build mode 31 | --- 32 | -------------------------------------------------------------------------------- /docs/introduction.md: -------------------------------------------------------------------------------- 1 | # introduction 2 | -------------------------------------------------------------------------------- /docs/introduction/getting-started.md: -------------------------------------------------------------------------------- 1 | # Getting Started 2 | 3 | 1. Install plugin (pnpm recommended 🚀). 4 | 5 | ```bash 6 | pnpm add vite-plugin-checker -D 7 | ``` 8 | 9 | If you are using Yarn or NPM 10 | 11 | ```bash 12 | yarn add vite-plugin-checker -D 13 | npm i vite-plugin-checker -D 14 | ``` 15 | 16 | 2. Add plugin to Vite config file and config the checker you need. We add TypeScript here as an example. See all available checkers [here](/checkers/overview). 17 | 18 | ```ts 19 | // vite.config.js 20 | import checker from 'vite-plugin-checker' 21 | export default { 22 | plugins: [ 23 | checker({ 24 | // e.g. use TypeScript check 25 | typescript: true, 26 | }), 27 | ], 28 | } 29 | ``` 30 | 31 | ::: tip 32 | If you'd prefer to not run the checkers during unit testing with Vitest, you can alter the config based on that. Example: 33 | 34 | ```ts 35 | // vite.config.js 36 | import checker from 'vite-plugin-checker' 37 | export default { 38 | plugins: [!process.env.VITEST ? checker({ typescript: true }) : undefined], 39 | } 40 | ``` 41 | 42 | ::: 43 | 44 | 3. You're all set. Open localhost page and start development 🚀. 45 | 46 | ::: tip 47 | It's recommended to open a browser for a better terminal flush, see [#27](https://github.com/fi3ework/vite-plugin-checker/pull/27). 48 | ::: 49 | 50 | ::: warning 51 | `server.ws.on` is introduced to Vite in [2.6.8](https://github.com/vitejs/vite/blob/main/packages/vite/CHANGELOG.md#268-2021-10-18). vite-plugin-checker relies on `server.ws.on` to make overlay visible after opening a new browser tab. 52 | ::: 53 | -------------------------------------------------------------------------------- /docs/introduction/introduction.md: -------------------------------------------------------------------------------- 1 | # About vite-plugin-checker 2 | 3 | A Vite plugin that can run TypeScript, VLS, vue-tsc, ESLint, Stylelint in worker thread to **add type checking and linting support** for Vite. 4 | 5 |
6 | 7 | 8 | 9 | 10 |
11 | 12 |

13 | screenshot 14 |

15 | 16 | > History version documentations [0.1](https://github.com/fi3ework/vite-plugin-checker/tree/v0.1.x), [0.2](https://github.com/fi3ework/vite-plugin-checker/tree/v0.2), [0.3](https://github.com/fi3ework/vite-plugin-checker/tree/v0.3.x), [0.4](https://github.com/fi3ework/vite-plugin-checker/tree/v0.4.x/docs). It's highly recommended to use latest version before 1.0.0, although there's some breaking changes in each version bumping, the plugin configuration is quite simple. 17 | 18 | ## Online playground 19 | 20 | | Examples | StackBlitz | 21 | | ------------------ | ---------------------------------------------------------------- | 22 | | Vue3 + vue-tsc | [⚡️ StackBlitz](https://stackblitz.com/edit/vitejs-vite-e8pddl) | 23 | | React + TypeScript | [⚡️ StackBlitz](https://stackblitz.com/edit/vitejs-vite-b4zcev) | 24 | | ESLint | [⚡️ StackBlitz](https://stackblitz.com/edit/vitejs-vite-l1ritu) | 25 | | Vue2 + VLS | [⚡️ StackBlitz](https://stackblitz.com/edit/vitejs-vite-kpffk5) | 26 | | Multiple | [⚡️ StackBlitz](https://stackblitz.com/edit/vitejs-vite-mb4ea6) | 27 | -------------------------------------------------------------------------------- /docs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "docs", 3 | "private": true, 4 | "type": "module", 5 | "scripts": { 6 | "build": "vitepress build", 7 | "dev": "vitepress dev" 8 | }, 9 | "dependencies": { 10 | "vitepress": "^1.6.3" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /docs/public/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fi3ework/vite-plugin-checker/78a8a7e1d748d333e77bddd3797734a13c8f0039/docs/public/logo.png -------------------------------------------------------------------------------- /knip.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://unpkg.com/knip@5/schema.json", 3 | "rules": { 4 | "duplicates": "off" 5 | }, 6 | "workspaces": { 7 | ".": { 8 | "entry": ["scripts/*", "playground/*"] 9 | }, 10 | "docs": { 11 | "entry": [".vitepress/config.ts"] 12 | }, 13 | "packages/vite-plugin-checker": { 14 | "entry": [ 15 | "tsup.config.ts", 16 | "src/**/*.ts", 17 | "src/checkers/vueTsc/languagePlugins.cjs", 18 | "__tests__/fixtures/tsDiagnostic.ts" 19 | ], 20 | "ignoreDependencies": [ 21 | "@vue/language-core", 22 | "eslint", 23 | "meow", 24 | "stylelint", 25 | "vls", 26 | "vue-tsc" 27 | ] 28 | }, 29 | "playground/backend-integration": { 30 | "entry": ["vite.config.js", "src/*.{js,ts,tsx}", "__test__/serve.js"] 31 | }, 32 | "playground/vls-vue2": { 33 | "entry": ["vetur.config.cjs", "src/**/*"], 34 | "ignoreDependencies": [ 35 | "tslib", 36 | "vue-class-component", 37 | "vue-property-decorator" 38 | ] 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /netlify.toml: -------------------------------------------------------------------------------- 1 | [build.environment] 2 | NPM_FLAGS = "--version" # prevent Netlify npm install 3 | [build] 4 | publish = "docs/.vitepress/dist" 5 | command = "npx pnpm i --store=node_modules/.pnpm-store --frozen-lockfile && npm run docs:build" 6 | ignore = "./scripts/docs-check.sh" 7 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.0.0", 3 | "private": true, 4 | "scripts": { 5 | "build": "pnpm -r --filter='./packages/**' run build", 6 | "build:test": "pnpm -r --filter='./packages/**' run build:test", 7 | "clean": "pnpm -r --filter='./packages/**' run clean", 8 | "dev": "pnpm -r --filter='./packages/**' --parallel run dev", 9 | "docs:build": "pnpm --filter docs run build", 10 | "docs:dev": "pnpm --filter docs run dev", 11 | "format": "biome format . --write", 12 | "postinstall": "simple-git-hooks install", 13 | "lint": "biome check . --diagnostic-level=warn", 14 | "publint": "pnpm build && pnpm -r --filter='vite-plugin-checker' exec publint", 15 | "sort-package-json": "npx sort-package-json \"packages/*/package.json\"", 16 | "test": "pnpm test-unit && pnpm test-serve && pnpm test-build", 17 | "test-build": "VITE_TEST_BUILD=1 vitest run -c vitest.config.e2e.ts", 18 | "test-knip": "knip", 19 | "test-serve": "vitest run -c vitest.config.e2e.ts", 20 | "test-unit": "vitest run", 21 | "test-unit:watch": "vitest", 22 | "type-check": "pnpm -r --parallel --filter \"vite-plugin-checker\" exec tsc --noEmit" 23 | }, 24 | "simple-git-hooks": { 25 | "pre-commit": "npx lint-staged" 26 | }, 27 | "lint-staged": { 28 | "package.json": "sort-package-json", 29 | "packages/**/*.{js,ts}": [ 30 | "biome check --write" 31 | ] 32 | }, 33 | "devDependencies": { 34 | "@biomejs/biome": "^1.9.4", 35 | "@tsconfig/esm": "^1.0.5", 36 | "@tsconfig/node20": "^20.1.5", 37 | "@tsconfig/strictest": "^2.0.5", 38 | "@types/babel__code-frame": "^7.0.6", 39 | "@types/node": "^20.17.32", 40 | "execa": "^9.5.3", 41 | "fast-json-stable-stringify": "^2.1.0", 42 | "knip": "^5.53.0", 43 | "lint-staged": "^15.5.2", 44 | "pkg-pr-new": "^0.0.51", 45 | "playwright-chromium": "^1.52.0", 46 | "publint": "^0.3.12", 47 | "rimraf": "^5.0.10", 48 | "simple-git-hooks": "^2.13.0", 49 | "sort-deep-object-arrays": "^1.1.2", 50 | "sort-package-json": "^3.1.0", 51 | "strip-ansi": "^7.1.0", 52 | "tiny-invariant": "^1.3.3", 53 | "tinyglobby": "^0.2.13", 54 | "typescript": "^5.8.3", 55 | "vite": "^6.3.4", 56 | "vitest": "^3.1.4" 57 | }, 58 | "packageManager": "pnpm@9.15.9" 59 | } 60 | -------------------------------------------------------------------------------- /packages/runtime/.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules/ 2 | /public/build/ 3 | 4 | .DS_Store 5 | -------------------------------------------------------------------------------- /packages/runtime/README.md: -------------------------------------------------------------------------------- 1 | # @vite-plugin-checker/runtime 2 | 3 | Runtime code for vite-plugin-checker, this package will be directly bundled into the vite-plugin-checker package in building process and hasn't be released to NPM for now. 4 | 5 | ## Development 6 | 7 | ### local 8 | 9 | Watch and compile code with mock diagnostics and html without vite-plugin-checker. 10 | 11 | ```bash 12 | pnpm dev-local 13 | pnpm preview 14 | ``` 15 | 16 | ### with vite-plugin-checker 17 | 18 | Watch and compile bundled JS to `../vite-plugin-checker/dist/@runtime/main.js`. Run `pnpm dev` in monorepo root will invoke below scripts. 19 | 20 | ```bash 21 | pnpm dev 22 | ``` 23 | -------------------------------------------------------------------------------- /packages/runtime/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /packages/runtime/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@vite-plugin-checker/runtime", 3 | "version": "0.0.0", 4 | "private": true, 5 | "description": "Runtime code for vite-plugin-checker", 6 | "sideEffects": [ 7 | "*.ts" 8 | ], 9 | "type": "module", 10 | "main": "dist/main.js", 11 | "scripts": { 12 | "build": "vue-tsc --noEmit && vite build", 13 | "build:test": "pnpm run build", 14 | "dev": "vite build --watch" 15 | }, 16 | "devDependencies": { 17 | "@vitejs/plugin-vue": "^5.2.4", 18 | "vite": "^6.3.4", 19 | "vue": "^3.5.13", 20 | "vue-tsc": "~2.2.10" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /packages/runtime/src/components/Checker.ce.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 23 | 24 | 34 | -------------------------------------------------------------------------------- /packages/runtime/src/components/List.ce.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 23 | 24 | 35 | -------------------------------------------------------------------------------- /packages/runtime/src/main.ts: -------------------------------------------------------------------------------- 1 | import { defineCustomElement } from 'vue' 2 | import App from './App.ce.vue' 3 | 4 | const ShadowElement = defineCustomElement(App) 5 | const overlayId = 'vite-plugin-checker-error-overlay' 6 | 7 | if (customElements && !customElements.get(overlayId)) { 8 | customElements.define(overlayId, ShadowElement) 9 | } 10 | 11 | export function inject({ 12 | base, 13 | overlayConfig, 14 | }: { 15 | base: string 16 | overlayConfig: Record 17 | }) { 18 | const overlayEle = new ShadowElement({ 19 | base, 20 | overlayConfig, 21 | }) 22 | document.body.appendChild(overlayEle) 23 | } 24 | -------------------------------------------------------------------------------- /packages/runtime/src/useChecker.ts: -------------------------------------------------------------------------------- 1 | import { ref } from 'vue' 2 | import { 3 | listenToCustomMessage, 4 | listenToReconnectMessage, 5 | prepareListen, 6 | } from './ws' 7 | 8 | const checkerResults = ref([]) 9 | 10 | function updateErrorOverlay(payloads: any) { 11 | const payloadArray = Array.isArray(payloads) ? payloads : [payloads] 12 | const nextCheckerResults = [ 13 | ...payloadArray, 14 | ...checkerResults.value.filter((existCheckerResult) => { 15 | return !payloadArray 16 | .map((p) => p.checkerId) 17 | .includes(existCheckerResult.checkerId) 18 | }), 19 | ] 20 | 21 | checkerResults.value = nextCheckerResults 22 | } 23 | 24 | function resumeErrorOverlay(data: any) { 25 | const payloadsToResume = data.map((d: any) => d.data) 26 | updateErrorOverlay(payloadsToResume) 27 | } 28 | 29 | export function useChecker() { 30 | const ws = prepareListen() 31 | listenToCustomMessage(updateErrorOverlay) 32 | listenToReconnectMessage(resumeErrorOverlay) 33 | ws.startListening() 34 | 35 | return { 36 | checkerResults, 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /packages/runtime/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /packages/runtime/src/ws.ts: -------------------------------------------------------------------------------- 1 | // #region 2 | // NOTE: sync modification with packages/vite-plugin-checker/client/index.ts 3 | const WS_CHECKER_ERROR_EVENT = 'vite-plugin-checker:error' 4 | const WS_CHECKER_RECONNECT_EVENT = 'vite-plugin-checker:reconnect' 5 | // #endregion 6 | 7 | const onCustomMessage: any[] = [] 8 | const onReconnectMessage: any[] = [] 9 | 10 | export function listenToCustomMessage(cb: (data: any) => any) { 11 | onCustomMessage.push(cb) 12 | } 13 | 14 | export function listenToReconnectMessage(cb: (data: any) => any) { 15 | onReconnectMessage.push(cb) 16 | } 17 | 18 | export function prepareListen() { 19 | const onMessage = async (data: any) => { 20 | switch (data.event) { 21 | case WS_CHECKER_ERROR_EVENT: 22 | for (const callbackfn of onCustomMessage) { 23 | callbackfn(data.data) 24 | } 25 | 26 | break 27 | case WS_CHECKER_RECONNECT_EVENT: 28 | for (const callbackfn of onReconnectMessage) { 29 | callbackfn(data.data) 30 | } 31 | 32 | break 33 | } 34 | } 35 | 36 | return { 37 | startListening: () => { 38 | if (import.meta.hot) { 39 | // listen server -> client messages 40 | import.meta.hot.on('vite-plugin-checker', (data: any) => { 41 | onMessage(data) 42 | }) 43 | 44 | // told server that vite-plugin-checker runtime has loaded 45 | // then server should send stored diagnostics to display overlay 46 | // NOTE: sync modification with packages /packages/vite-plugin-checker/src/main.ts 47 | import.meta.hot.send('vite-plugin-checker', { event: 'runtime-loaded' }) 48 | } 49 | }, 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /packages/runtime/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "useDefineForClassFields": true, 5 | "module": "ESNext", 6 | "lib": ["ES2020", "DOM", "DOM.Iterable"], 7 | "skipLibCheck": true, 8 | 9 | /* Bundler mode */ 10 | "moduleResolution": "bundler", 11 | "allowImportingTsExtensions": true, 12 | "resolveJsonModule": true, 13 | "isolatedModules": true, 14 | "noEmit": true, 15 | "jsx": "preserve", 16 | 17 | /* Linting */ 18 | "strict": true, 19 | "noUnusedLocals": true, 20 | "noUnusedParameters": true, 21 | "noFallthroughCasesInSwitch": true 22 | }, 23 | "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"], 24 | "references": [{ "path": "./tsconfig.node.json" }] 25 | } 26 | -------------------------------------------------------------------------------- /packages/runtime/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "skipLibCheck": true, 5 | "module": "ESNext", 6 | "moduleResolution": "bundler", 7 | "allowSyntheticDefaultImports": true 8 | }, 9 | "include": ["vite.config.ts"] 10 | } 11 | -------------------------------------------------------------------------------- /packages/runtime/vite.config.ts: -------------------------------------------------------------------------------- 1 | import vue from '@vitejs/plugin-vue' 2 | import { defineConfig } from 'vite' 3 | 4 | import path from 'node:path' 5 | import { fileURLToPath } from 'node:url' 6 | 7 | const __filename = fileURLToPath(import.meta.url) 8 | const __dirname = path.dirname(__filename) 9 | 10 | export default defineConfig({ 11 | plugins: [vue()], 12 | define: { 13 | 'import.meta.hot': 'import.meta.hot', 14 | }, 15 | build: { 16 | minify: false, // for easy to debug and the size is not that important 17 | emptyOutDir: true, 18 | outDir: path.resolve(__dirname, '../vite-plugin-checker/dist/@runtime'), 19 | lib: { 20 | entry: path.resolve(__dirname, 'src/main.ts'), 21 | formats: ['es'], 22 | fileName: () => 'main.js', 23 | }, 24 | }, 25 | }) 26 | -------------------------------------------------------------------------------- /packages/vite-plugin-checker/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # vite-plugin-checker 2 | 3 | ## 0.7.2 4 | 5 | ### Patch Changes 6 | 7 | - 568b782: Added basic support for Biome 8 | - 82972e0: fix: tweak biome configs 9 | 10 | ## 0.7.1 11 | 12 | ### Patch Changes 13 | 14 | - 909182e: Remove extraneous non props attributes warning 15 | - e881c44: Bump @vitejs/plugin-vue to resolve runtime warning, see #346 16 | - 80ca69c: Resolve optionaltor from ESLint path, do not requires to install optionator anymore 17 | - 78fc007: Throw error and hint user when vue-tsc working with typescript lower than 5.0.0 18 | - 52423b2: sync runTsc https://github.com/volarjs/volar.js/blob/630f31118d3986c00cc730eb83cd896709fd547e/packages/typescript/lib/quickstart/runTsc.ts 19 | - 2a0af74: refactor: reuse codeFrame helper in logger and deduplicate code 20 | - 7d985e7: refactor: import `@volar/typescript` from `vue-tsc` 21 | 22 | ## 0.7.0 23 | 24 | ### Minor Changes 25 | 26 | - 0747729: fix: compatibility with vue-tsc 2.x 27 | 28 | ## 0.6.4 29 | 30 | ### Patch Changes 31 | 32 | - 83e1028: Remove lodash-es from dependencies 33 | - b0cce16: fix: add explicitly ltr direction to the overlay 34 | 35 | ## 0.6.3 36 | 37 | ### Patch Changes 38 | 39 | - e5a26d6: feat: support initially open overlay for errors 40 | - bc4fa05: Remove lodash per method packages 41 | - c5d5109: support eslint flat config 42 | 43 | ## 0.6.2 44 | 45 | ### Patch Changes 46 | 47 | - ab70e33: fix config.overlay.panelStyle not be applied at runtime 48 | - bad24c7: add optional global configuration of root directory (#262) 49 | 50 | ## 0.6.1 51 | 52 | ### Patch Changes 53 | 54 | - ec4366d: use `virtual:` for virtual module 55 | - 154ca0f: Able to resolve tsconfig when only root specified in build mode, as well as vue-tsc. 56 | - b3e0055: Should respect `server.origin` when it's provided. 57 | - e063617: Migrate runtime UI from svelte to vue, user should not aware this. 58 | -------------------------------------------------------------------------------- /packages/vite-plugin-checker/__tests__/codeFrame.spec.ts: -------------------------------------------------------------------------------- 1 | import { describe, expect, it } from 'vitest' 2 | 3 | import { lineColLocToBabelLoc, tsLikeLocToBabelLoc } from '../src/codeFrame' 4 | 5 | describe('code frame', () => { 6 | it('should add 1 offset to TS location', () => { 7 | const babelLoc = tsLikeLocToBabelLoc({ 8 | start: { line: 1, character: 2 }, 9 | end: { line: 3, character: 4 }, 10 | }) 11 | 12 | expect(babelLoc).toEqual({ 13 | start: { line: 2, column: 3 }, 14 | end: { line: 4, column: 5 }, 15 | }) 16 | }) 17 | 18 | it('transform location without offset', () => { 19 | const babelLoc = lineColLocToBabelLoc({ 20 | line: 1, 21 | column: 2, 22 | endLine: 3, 23 | endColumn: 4, 24 | }) 25 | 26 | expect(babelLoc).toEqual({ 27 | start: { line: 1, column: 2 }, 28 | end: { line: 3, column: 4 }, 29 | }) 30 | }) 31 | }) 32 | -------------------------------------------------------------------------------- /packages/vite-plugin-checker/__tests__/logger.spec.ts: -------------------------------------------------------------------------------- 1 | import strip from 'strip-ansi' 2 | import { describe, expect, it } from 'vitest' 3 | 4 | import { 5 | diagnosticToTerminalLog, 6 | normalizeEslintDiagnostic, 7 | } from '../src/logger' 8 | import { 9 | error1 as eslintError1, 10 | eslintResult1, 11 | warning1 as eslintWarning1, 12 | } from './fixtures/eslintDiagnostic' 13 | 14 | describe('logger', () => { 15 | describe('diagnosticToTerminalLog', () => { 16 | it('get error', () => { 17 | const received = strip(diagnosticToTerminalLog(eslintError1, 'ESLint')) 18 | expect(received).toMatchSnapshot() 19 | }) 20 | 21 | it('get warning', () => { 22 | const received = strip(diagnosticToTerminalLog(eslintWarning1, 'ESLint')) 23 | expect(received).toMatchSnapshot() 24 | }) 25 | }) 26 | 27 | describe('normalizeEslintDiagnostic', () => { 28 | it('get multiple diagnostics', () => { 29 | const received = normalizeEslintDiagnostic(eslintResult1) 30 | expect(received).toMatchSnapshot() 31 | }) 32 | }) 33 | }) 34 | -------------------------------------------------------------------------------- /packages/vite-plugin-checker/__tests__/vlsConfig.spec.ts: -------------------------------------------------------------------------------- 1 | import path from 'node:path' 2 | import { describe, expect, it } from 'vitest' 3 | import { ShutdownRequest } from 'vscode-languageserver/node' 4 | import { URI } from 'vscode-uri' 5 | 6 | import { 7 | logLevel2Severity, 8 | prepareClientConnection, 9 | } from '../src/checkers/vls/diagnostics' 10 | 11 | async function testVslConfig(overrideConfig?: any) { 12 | const workspaceUri = URI.file(path.join(__dirname, 'fixtures')) 13 | const { clientConnection, serverConnection, vls, up, down } = 14 | await prepareClientConnection(workspaceUri, logLevel2Severity.WARN, { 15 | watch: false, 16 | verbose: false, 17 | config: overrideConfig || null, 18 | }) 19 | 20 | // @ts-expect-error 21 | expect(vls.workspaceConfig).toMatchSnapshot() 22 | 23 | // TODO: this test case will let Jest hang with out --forceExit option 24 | // current enable forceExit but we should find which resource is not released 25 | await clientConnection.sendRequest(ShutdownRequest.type) 26 | clientConnection.dispose() 27 | clientConnection.end() 28 | serverConnection.dispose() 29 | up.destroy() 30 | down.destroy() 31 | vls.dispose() 32 | } 33 | 34 | describe('VLS config', () => { 35 | it('default config', async () => { 36 | await testVslConfig() 37 | }) 38 | 39 | it('customized config', async () => { 40 | await testVslConfig({ 41 | vetur: { 42 | validation: { 43 | template: false, 44 | templateProps: false, 45 | interpolation: false, 46 | style: false, 47 | }, 48 | }, 49 | }) 50 | }) 51 | }) 52 | -------------------------------------------------------------------------------- /packages/vite-plugin-checker/src/Checker.ts: -------------------------------------------------------------------------------- 1 | import invariant from 'tiny-invariant' 2 | import { isInVitestEntryThread, isMainThread } from './utils.js' 3 | 4 | import { type Script, createScript } from './worker.js' 5 | 6 | import type { 7 | BuildInCheckerNames, 8 | BuildInCheckers, 9 | CreateDiagnostic, 10 | ServeAndBuildChecker, 11 | } from './types.js' 12 | 13 | if (!(isMainThread || isInVitestEntryThread)) { 14 | process.stdout.isTTY = true 15 | } 16 | 17 | interface CheckerMeta { 18 | name: T 19 | absFilePath: string 20 | createDiagnostic: CreateDiagnostic 21 | build: ServeAndBuildChecker['build'] 22 | script?: Script 23 | } 24 | 25 | export abstract class Checker 26 | implements CheckerMeta 27 | { 28 | public static logger: ((...v: string[]) => unknown)[] = [] 29 | 30 | public static log(...args: any[]) { 31 | for (const fn of Checker.logger) { 32 | fn(...args) 33 | } 34 | } 35 | 36 | public name: T 37 | public absFilePath: string 38 | public createDiagnostic: CreateDiagnostic 39 | public build: ServeAndBuildChecker['build'] 40 | public script?: Script 41 | 42 | public constructor({ 43 | name, 44 | absFilePath, 45 | createDiagnostic, 46 | build, 47 | }: CheckerMeta) { 48 | this.name = name 49 | this.absFilePath = absFilePath 50 | this.build = build 51 | this.createDiagnostic = createDiagnostic 52 | this.build = build 53 | } 54 | 55 | public prepare() { 56 | const script = createScript>({ 57 | absFilename: this.absFilePath, 58 | buildBin: this.build.buildBin, 59 | serverChecker: { createDiagnostic: this.createDiagnostic }, 60 | })! 61 | 62 | this.script = script 63 | return script 64 | } 65 | 66 | public initMainThread() { 67 | invariant( 68 | this.script, 69 | `script should be created in 'prepare', but got ${this.script}`, 70 | ) 71 | 72 | if (isMainThread || isInVitestEntryThread) { 73 | const createServeAndBuild = this.script.mainScript() 74 | return createServeAndBuild 75 | } 76 | 77 | return 78 | } 79 | 80 | public initWorkerThread() { 81 | invariant( 82 | this.script, 83 | `script should be created in 'prepare', but got ${this.script}`, 84 | ) 85 | 86 | if (!(isMainThread || isInVitestEntryThread)) { 87 | this.script.workerScript() 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /packages/vite-plugin-checker/src/FileDiagnosticManager.ts: -------------------------------------------------------------------------------- 1 | import type { NormalizedDiagnostic } from './logger.js' 2 | 3 | class FileDiagnosticManager { 4 | public diagnostics: NormalizedDiagnostic[] = [] 5 | 6 | /** 7 | * Initialize and reset the diagnostics array 8 | */ 9 | public initWith(diagnostics: NormalizedDiagnostic[]) { 10 | this.diagnostics = [...diagnostics] 11 | } 12 | 13 | public getDiagnostics(fileName?: string) { 14 | if (fileName) { 15 | return this.diagnostics.filter((f) => f.id === fileName) 16 | } 17 | 18 | return this.diagnostics 19 | } 20 | 21 | public updateByFileId(fileId: string, next: NormalizedDiagnostic[] | null) { 22 | this.diagnostics = this.diagnostics.filter((d) => d.id !== fileId) 23 | 24 | if (next?.length) { 25 | this.diagnostics.push(...next) 26 | } 27 | } 28 | } 29 | 30 | export { FileDiagnosticManager } 31 | -------------------------------------------------------------------------------- /packages/vite-plugin-checker/src/checkers/eslint/cli.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | 3 | /** 4 | * This file is directly copied from https://github.com/eslint/eslint/blob/6f940c3ce715327f282c197d0f71b91848e5d83d/lib/cli.js 5 | * 6 | * Usually, developer rarely use JS programming API to run ESLint. So we let 7 | * developers to write their own ESLint commands just like in CI or lint-staged. 8 | * And the config will be translated and pass to `new ESLint(translatedOptions)`. 9 | * So in build mode, it's the same as the command you pass in. 10 | * In dev mode, some flag will be ignored (such as `max-warnings`) because it 11 | * will be only respected in ESLint CLI. 12 | */ 13 | 14 | // @ts-expect-error 15 | function quietFixPredicate(message) { 16 | return message.severity === 2 17 | } 18 | 19 | export function translateOptions({ 20 | cache, 21 | cacheFile, 22 | cacheLocation, 23 | cacheStrategy, 24 | config, 25 | env, 26 | errorOnUnmatchedPattern, 27 | eslintrc, 28 | ext, 29 | fix, 30 | fixDryRun, 31 | fixType, 32 | global, 33 | ignore, 34 | ignorePath, 35 | ignorePattern, 36 | inlineConfig, 37 | parser, 38 | parserOptions, 39 | plugin, 40 | quiet, 41 | reportUnusedDisableDirectives, 42 | resolvePluginsRelativeTo, 43 | rule, 44 | rulesdir, 45 | }: any) { 46 | return { 47 | allowInlineConfig: inlineConfig, 48 | cache, 49 | cacheLocation: cacheLocation || cacheFile, 50 | cacheStrategy, 51 | errorOnUnmatchedPattern, 52 | extensions: ext, 53 | fix: (fix || fixDryRun) && (quiet ? quietFixPredicate : true), 54 | fixTypes: fixType, 55 | ignore, 56 | ignorePath, 57 | overrideConfig: { 58 | env: 59 | // @ts-expect-error 60 | env?.reduce((obj, name) => { 61 | obj[name] = true 62 | return obj 63 | }, {}), 64 | // @ts-expect-error 65 | globals: global?.reduce((obj, name) => { 66 | if (name.endsWith(':true')) { 67 | obj[name.slice(0, -5)] = 'writable' 68 | } else { 69 | obj[name] = 'readonly' 70 | } 71 | return obj 72 | }, {}), 73 | ignorePatterns: ignorePattern, 74 | parser, 75 | parserOptions, 76 | plugins: plugin, 77 | rules: rule, 78 | }, 79 | overrideConfigFile: config, 80 | reportUnusedDisableDirectives: reportUnusedDisableDirectives 81 | ? 'error' 82 | : void 0, 83 | resolvePluginsRelativeTo, 84 | rulePaths: rulesdir, 85 | useEslintrc: eslintrc, 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /packages/vite-plugin-checker/src/checkers/stylelint/argv.ts: -------------------------------------------------------------------------------- 1 | // copied from https://github.com/jonschlinkert/is-plain-object/blob/master/is-plain-object.js 2 | // to make less breaking change, we'll make it a dependency before v1.0.0 3 | 4 | export { parseArgsStringToArgv as default, parseArgsStringToArgv } 5 | function parseArgsStringToArgv( 6 | value: string, 7 | env?: string, 8 | file?: string, 9 | ): string[] { 10 | // ([^\s'"]([^\s'"]*(['"])([^\3]*?)\3)+[^\s'"]*) Matches nested quotes until the first space outside of quotes 11 | 12 | // [^\s'"]+ or Match if not a space ' or " 13 | 14 | // (['"])([^\5]*?)\5 or Match "quoted text" without quotes 15 | // `\3` and `\5` are a backreference to the quote style (' or ") captured 16 | const myRegexp = 17 | // @ts-expect-error Bypass typescript validation 18 | /([^\s'"]([^\s'"]*(['"])([^\3]*?)\3)+[^\s'"]*)|[^\s'"]+|(['"])([^\5]*?)\5/gi 19 | const myString = value 20 | const myArray: string[] = [] 21 | if (env) { 22 | myArray.push(env) 23 | } 24 | if (file) { 25 | myArray.push(file) 26 | } 27 | let match: RegExpExecArray | null 28 | do { 29 | // Each call to exec returns the next regex match as an array 30 | match = myRegexp.exec(myString) 31 | if (match !== null) { 32 | // Index 1 in the array is the captured group if it exists 33 | // Index 0 is the matched text, which we use if no captured group exists 34 | myArray.push(firstString(match[1], match[6], match[0])!) 35 | } 36 | } while (match !== null) 37 | 38 | return myArray 39 | } 40 | 41 | // Accepts any number of arguments, and returns the first one that is a string 42 | // (even an empty string) 43 | // @ts-ignore 44 | function firstString(...args: Array): string | undefined { 45 | // eslint-disable-next-line @typescript-eslint/prefer-for-of 46 | for (let i = 0; i < args.length; i++) { 47 | const arg = args[i] 48 | if (typeof arg === 'string') { 49 | return arg 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /packages/vite-plugin-checker/src/checkers/vls/typings.d.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fi3ework/vite-plugin-checker/78a8a7e1d748d333e77bddd3797734a13c8f0039/packages/vite-plugin-checker/src/checkers/vls/typings.d.ts -------------------------------------------------------------------------------- /packages/vite-plugin-checker/src/checkers/vueTsc/languagePlugins.cjs: -------------------------------------------------------------------------------- 1 | const path = require('node:path') 2 | 3 | const vueTscDir = path.dirname(require.resolve('vue-tsc/package.json')) 4 | const vue = /** @type {typeof import('@vue/language-core')} */ ( 5 | require(require.resolve('@vue/language-core', { paths: [vueTscDir] })) 6 | ) 7 | const windowsPathReg = /\\/g 8 | 9 | const removeEmitGlobalTypesRegexp = 10 | /^[^\n]*__VLS_globalTypesStart[\w\W]*__VLS_globalTypesEnd[^\n]*\n?$/gm 11 | 12 | /** 13 | * @param dts {string} 14 | * @returns {string} 15 | */ 16 | function removeEmitGlobalTypes(dts) { 17 | return dts.replace(removeEmitGlobalTypesRegexp, '') 18 | } 19 | 20 | // #region copied from https://github.com/vuejs/language-tools/blob/0781998a29f176ad52c30d3139d5c78a5688bd5d/packages/tsc/index.ts 21 | /** 22 | * @param {typeof import('typescript')} ts 23 | * @param {import('typescript').CreateProgramOptions} options 24 | */ 25 | exports.getLanguagePlugins = (ts, options) => { 26 | const { configFilePath } = options.options 27 | const vueOptions = 28 | typeof configFilePath === 'string' 29 | ? vue.createParsedCommandLine( 30 | ts, 31 | ts.sys, 32 | configFilePath.replace(windowsPathReg, '/'), 33 | ).vueOptions 34 | : vue.resolveVueCompilerOptions({}) 35 | const host = /** @type {import('typescript').CompilerHost} */ (options.host) 36 | const writeFile = host.writeFile.bind(host) 37 | host.writeFile = (fileName, contents, ...args) => { 38 | return writeFile(fileName, removeEmitGlobalTypes(contents), ...args) 39 | } 40 | const vueLanguagePlugin = vue.createVueLanguagePlugin( 41 | ts, 42 | options.options, 43 | vueOptions, 44 | (id) => id, 45 | ) 46 | return [vueLanguagePlugin] 47 | } 48 | // #endregion 49 | -------------------------------------------------------------------------------- /packages/vite-plugin-checker/src/client/index.ts: -------------------------------------------------------------------------------- 1 | import fs from 'node:fs' 2 | import { createRequire } from 'node:module' 3 | import type { SharedConfig } from '../types' 4 | const _require = createRequire(import.meta.url) 5 | 6 | export const RUNTIME_CLIENT_RUNTIME_PATH = '/@vite-plugin-checker-runtime' 7 | export const RUNTIME_CLIENT_ENTRY_PATH = '/@vite-plugin-checker-runtime-entry' 8 | 9 | export const wrapVirtualPrefix = (id: `/${string}`): `virtual:${string}` => 10 | `virtual:${id.slice('/'.length)}` 11 | export const composePreambleCode = ({ 12 | baseWithOrigin = '/', 13 | overlayConfig, 14 | }: { 15 | baseWithOrigin: string 16 | overlayConfig: SharedConfig['overlay'] 17 | }) => ` 18 | import { inject } from "${baseWithOrigin}${RUNTIME_CLIENT_RUNTIME_PATH.slice(1)}"; 19 | inject({ 20 | overlayConfig: ${JSON.stringify(overlayConfig)}, 21 | base: "${baseWithOrigin}", 22 | }); 23 | ` 24 | 25 | // #region 26 | // NOTE: sync modification with packages/runtime/src/ws.js 27 | export const WS_CHECKER_ERROR_EVENT = 'vite-plugin-checker:error' 28 | export const WS_CHECKER_RECONNECT_EVENT = 'vite-plugin-checker:reconnect' 29 | // #endregion 30 | 31 | export const runtimeSourceFilePath = _require.resolve('../@runtime/main.js') 32 | export const runtimeCode = `${fs.readFileSync(runtimeSourceFilePath, 'utf-8')};` 33 | -------------------------------------------------------------------------------- /packages/vite-plugin-checker/src/codeFrame.ts: -------------------------------------------------------------------------------- 1 | import os from 'node:os' 2 | 3 | import { type SourceLocation, codeFrameColumns } from '@babel/code-frame' 4 | 5 | /** 6 | * Create a code frame from source code and location 7 | * @param source source code 8 | * @param location babel compatible location to highlight 9 | */ 10 | export function createFrame(source: string, location: SourceLocation): string { 11 | return codeFrameColumns(source, location, { 12 | // worker tty did not fork parent process stdout, let's make a workaround 13 | forceColor: true, 14 | }) 15 | .split('\n') 16 | .map((line) => ` ${line}`) 17 | .join(os.EOL) 18 | } 19 | 20 | export function tsLikeLocToBabelLoc( 21 | tsLoc: Record< 22 | 'start' | 'end', 23 | { line: number; character: number } /** 0-based */ 24 | >, 25 | ): SourceLocation { 26 | return { 27 | start: { line: tsLoc.start.line + 1, column: tsLoc.start.character + 1 }, 28 | end: { line: tsLoc.end.line + 1, column: tsLoc.end.character + 1 }, 29 | } 30 | } 31 | 32 | export function lineColLocToBabelLoc(d: { 33 | line: number 34 | column: number 35 | endLine?: number 36 | endColumn?: number 37 | }): SourceLocation { 38 | return { 39 | start: { line: d.line, column: d.column }, 40 | end: { line: d.endLine || 0, column: d.endColumn }, 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /packages/vite-plugin-checker/src/glob.ts: -------------------------------------------------------------------------------- 1 | import { type Stats, statSync } from 'node:fs' 2 | import { join, relative, resolve } from 'node:path' 3 | import picomatch from 'picomatch' 4 | 5 | export function createIgnore(_root: string, pattern: string | string[] = []) { 6 | const paths = Array.isArray(pattern) ? pattern : [pattern] 7 | const root = _root.replace(/\\/g, '/') 8 | 9 | const globs = paths 10 | .flatMap((f) => { 11 | const resolvedPath = resolve(root, f) 12 | const relativePath = relative(root, resolvedPath).replace(/\\/g, '/') 13 | try { 14 | const isDirectory = 15 | !relativePath.includes('*') && statSync(resolvedPath).isDirectory() 16 | if (isDirectory) { 17 | return [relativePath, join(relativePath, '**/*').replace(/\\/g, '/')] 18 | } 19 | } catch {} 20 | return [relativePath] 21 | }) 22 | .filter(Boolean) 23 | 24 | const matcher = picomatch(globs, { cwd: root }) 25 | 26 | return (path: string, _stats?: Stats) => { 27 | if (path.includes('node_modules')) { 28 | return true 29 | } 30 | const relativePath = relative(root, path).replace(/\\/g, '/') 31 | try { 32 | return ( 33 | !!relativePath && 34 | !matcher(relativePath) && 35 | !(_stats ?? statSync(path)).isDirectory() 36 | ) 37 | } catch { 38 | // do not ignore files that have been deleted as the watcher is iterating on them 39 | return false 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /packages/vite-plugin-checker/src/utils.ts: -------------------------------------------------------------------------------- 1 | import { isMainThread as _isMainThread, threadId } from 'node:worker_threads' 2 | 3 | // since vitest run all cases in worker thread, we should compatible with it to pass E2E tests 4 | 5 | // @ts-expect-error use Vitest 6 | export const isInVitestEntryThread = threadId === 0 && process.env.VITEST 7 | export const isMainThread = _isMainThread || isInVitestEntryThread 8 | -------------------------------------------------------------------------------- /packages/vite-plugin-checker/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "@tsconfig/strictest/tsconfig", 4 | "@tsconfig/node20/tsconfig", 5 | "@tsconfig/esm/tsconfig" 6 | ], 7 | "compilerOptions": { 8 | "allowJs": true, 9 | "isolatedModules": true, 10 | "noUnusedParameters": false, 11 | "exactOptionalPropertyTypes": false 12 | }, 13 | "include": ["src", "__tests__"], 14 | "exclude": ["src/@runtime"] 15 | } 16 | -------------------------------------------------------------------------------- /packages/vite-plugin-checker/tsup.config.ts: -------------------------------------------------------------------------------- 1 | import { copyFile } from 'node:fs/promises' 2 | import { defineConfig } from 'tsup' 3 | 4 | export default defineConfig({ 5 | format: ['esm'], 6 | outDir: 'dist', 7 | async onSuccess() { 8 | await copyFile( 9 | 'src/checkers/vueTsc/languagePlugins.cjs', 10 | 'dist/checkers/vueTsc/languagePlugins.cjs', 11 | ) 12 | }, 13 | entry: ['src', '!src/checkers/vueTsc/languagePlugins.cjs'], 14 | splitting: false, 15 | bundle: false, 16 | sourcemap: true, 17 | clean: true, 18 | target: 'node14', 19 | platform: 'node', 20 | dts: true, 21 | }) 22 | -------------------------------------------------------------------------------- /playground/backend-integration/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /playground/backend-integration/__test__/__snapshots__/test.spec.ts.snap: -------------------------------------------------------------------------------- 1 | // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html 2 | 3 | exports[`backend-integration > serve > get initial error 1`] = `"[{"checkerId":"TypeScript","frame":" 4 |/n 5 | function App() {/n > 6 | const [count, setCount] = useState(0)/n | ^/n 7 |/n 8 | return (/n 9 |
","id":"/playground-temp/backend-integration/src/App.tsx","level":1,"loc":{"column":46,"file":"/playground-temp/backend-integration/src/App.tsx","line":6},"message":"Argument of type 'number' is not assignable to parameter of type 'string | (() => string)'.","stack":""}]"`; 4 | 5 | exports[`backend-integration > serve > get initial error 2`] = ` 6 | [ 7 | " 8 | ERROR(TypeScript) Argument of type 'number' is not assignable to parameter of type 'string | (() => string)'. 9 | FILE /playground-temp/backend-integration/src/App.tsx:6:46 10 | 11 | 4 | 12 | 5 | function App() { 13 | > 6 | const [count, setCount] = useState(0) 14 | | ^ 15 | 7 | 16 | 8 | return ( 17 | 9 |
18 | 19 | [TypeScript] Found 1 error. Watching for file changes.", 20 | ] 21 | `; 22 | -------------------------------------------------------------------------------- /playground/backend-integration/__test__/serve.js: -------------------------------------------------------------------------------- 1 | // this is automatically detected by playground/vitestSetup.ts and will replace 2 | // the default e2e test serve behavior 3 | 4 | import path from 'node:path' 5 | // import kill from 'kill-port' 6 | // import { hmrPorts, ports } from '~utils' 7 | import { fileURLToPath } from 'url' 8 | 9 | const isTest = process.env.VITEST 10 | const __dirname = path.dirname(fileURLToPath(import.meta.url)) 11 | 12 | export async function serve() { 13 | // await kill(port) 14 | const rootDir = path.resolve(__dirname, '../') 15 | 16 | const { createServer, port } = await import(path.resolve(rootDir, 'server.js')) 17 | const { app, viteDevServer } = await createServer(rootDir, port) 18 | 19 | return new Promise((resolve, reject) => { 20 | try { 21 | const server = app.listen(port, () => { 22 | resolve({ 23 | // for test teardown 24 | async close() { 25 | await new Promise((resolve) => { 26 | server.close(resolve) 27 | }) 28 | if (viteDevServer) { 29 | await viteDevServer.close() 30 | } 31 | }, 32 | viteDevServer, 33 | port, 34 | }) 35 | }) 36 | } catch (e) { 37 | reject(e) 38 | } 39 | }) 40 | } 41 | 42 | // serve() 43 | -------------------------------------------------------------------------------- /playground/backend-integration/__test__/test.spec.ts: -------------------------------------------------------------------------------- 1 | import stringify from 'fast-json-stable-stringify' 2 | import { describe, expect, it } from 'vitest' 3 | 4 | import { 5 | diagnostics, 6 | expectStderrContains, 7 | isBuild, 8 | isServe, 9 | log, 10 | sleepForServerReady, 11 | stripedLog, 12 | } from '../../testUtils' 13 | 14 | describe('backend-integration', () => { 15 | describe.runIf(isServe)('serve', () => { 16 | it('get initial error', async () => { 17 | await sleepForServerReady() 18 | 19 | expect(stringify(diagnostics)).toMatchSnapshot() 20 | expect(stripedLog).toMatchSnapshot() 21 | }) 22 | }) 23 | 24 | describe.runIf(isBuild)('build', () => { 25 | it('should fail', async () => { 26 | const expectedMsg = `TS2345: Argument of type 'number' is not assignable to parameter of type 'string | (() => string)'` 27 | expectStderrContains(log, expectedMsg) 28 | }) 29 | }) 30 | }) 31 | -------------------------------------------------------------------------------- /playground/backend-integration/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Vite + React + TS 7 | 8 | 9 |

Title on static server

10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /playground/backend-integration/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@playground/backend-integration", 3 | "version": "0.0.0", 4 | "private": true, 5 | "type": "module", 6 | "scripts": { 7 | "build": "vite build", 8 | "dev": "node server.js", 9 | "preview": "vite preview" 10 | }, 11 | "dependencies": { 12 | "react": "^19.1.0", 13 | "react-dom": "^19.1.0" 14 | }, 15 | "devDependencies": { 16 | "@types/express": "^5.0.2", 17 | "@types/react": "^19.1.3", 18 | "@types/react-dom": "^19.1.3", 19 | "@vitejs/plugin-react": "^4.4.1", 20 | "express": "^5.1.0", 21 | "http-proxy-middleware": "^3.0.5", 22 | "typescript": "^5.8.3", 23 | "vite": "^6.3.4", 24 | "vite-plugin-checker": "workspace:*" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /playground/backend-integration/public/vite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /playground/backend-integration/server.js: -------------------------------------------------------------------------------- 1 | import fs from 'node:fs' 2 | import path from 'node:path' 3 | import { fileURLToPath } from 'node:url' 4 | import express from 'express' 5 | import { createProxyMiddleware } from 'http-proxy-middleware' 6 | import { createServer as createViteServer } from 'vite' 7 | const __dirname = path.dirname(fileURLToPath(import.meta.url)) 8 | 9 | const rootDir = path.resolve(__dirname) 10 | export const port = 3008 11 | 12 | export async function createServer() { 13 | const app = express() 14 | const viteDevServer = await createViteServer({ root: rootDir }) 15 | 16 | let html = await fs.promises.readFile(path.resolve(__dirname, 'index.html'), 'utf-8') 17 | 18 | app.get('/', async (req, res) => { 19 | html = html.replace( 20 | '', 21 | ` 22 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | ` 36 | ) 37 | res.status(200).set({ 'Content-Type': 'text/html' }).send(html) 38 | }) 39 | 40 | app.use('/', createProxyMiddleware({ target: 'http://127.0.0.1:5173', changeOrigin: true })) 41 | 42 | return { app, viteDevServer } 43 | } 44 | 45 | const isTest = process.env.VITEST 46 | 47 | if (!isTest) { 48 | createServer().then(({ app, viteDevServer }) => { 49 | viteDevServer.listen() 50 | app.listen(port, () => { 51 | console.log('http://localhost:5173') 52 | }) 53 | }) 54 | } 55 | -------------------------------------------------------------------------------- /playground/backend-integration/src/App.css: -------------------------------------------------------------------------------- 1 | #root { 2 | max-width: 1280px; 3 | margin: 0 auto; 4 | padding: 2rem; 5 | text-align: center; 6 | } 7 | 8 | .logo { 9 | height: 6em; 10 | padding: 1.5em; 11 | will-change: filter; 12 | } 13 | .logo:hover { 14 | filter: drop-shadow(0 0 2em #646cffaa); 15 | } 16 | .logo.react:hover { 17 | filter: drop-shadow(0 0 2em #61dafbaa); 18 | } 19 | 20 | @keyframes logo-spin { 21 | from { 22 | transform: rotate(0deg); 23 | } 24 | to { 25 | transform: rotate(360deg); 26 | } 27 | } 28 | 29 | @media (prefers-reduced-motion: no-preference) { 30 | a:nth-of-type(2) .logo { 31 | animation: logo-spin infinite 20s linear; 32 | } 33 | } 34 | 35 | .card { 36 | padding: 2em; 37 | } 38 | 39 | .read-the-docs { 40 | color: #888; 41 | } 42 | -------------------------------------------------------------------------------- /playground/backend-integration/src/App.tsx: -------------------------------------------------------------------------------- 1 | import { useState } from 'react' 2 | import reactLogo from './assets/react.svg' 3 | import './App.css' 4 | 5 | function App() { 6 | const [count, setCount] = useState(0) 7 | 8 | return ( 9 |
10 | 18 |

Vite + React

19 |
20 | 21 |

22 | Edit src/App.tsx and save to test HMR 23 |

24 |
25 |

Click on the Vite and React logos to learn more

26 |
27 | ) 28 | } 29 | 30 | export default App 31 | -------------------------------------------------------------------------------- /playground/backend-integration/src/index.css: -------------------------------------------------------------------------------- 1 | :root { 2 | font-family: Inter, Avenir, Helvetica, Arial, sans-serif; 3 | font-size: 16px; 4 | line-height: 24px; 5 | font-weight: 400; 6 | 7 | color-scheme: light dark; 8 | color: rgba(255, 255, 255, 0.87); 9 | background-color: #242424; 10 | 11 | font-synthesis: none; 12 | text-rendering: optimizeLegibility; 13 | -webkit-font-smoothing: antialiased; 14 | -moz-osx-font-smoothing: grayscale; 15 | -webkit-text-size-adjust: 100%; 16 | } 17 | 18 | a { 19 | font-weight: 500; 20 | color: #646cff; 21 | text-decoration: inherit; 22 | } 23 | a:hover { 24 | color: #535bf2; 25 | } 26 | 27 | body { 28 | margin: 0; 29 | place-items: center; 30 | min-width: 320px; 31 | min-height: 100vh; 32 | } 33 | 34 | h1 { 35 | font-size: 3.2em; 36 | line-height: 1.1; 37 | } 38 | 39 | button { 40 | border-radius: 8px; 41 | border: 1px solid transparent; 42 | padding: 0.6em 1.2em; 43 | font-size: 1em; 44 | font-weight: 500; 45 | font-family: inherit; 46 | background-color: #1a1a1a; 47 | cursor: pointer; 48 | transition: border-color 0.25s; 49 | } 50 | button:hover { 51 | border-color: #646cff; 52 | } 53 | button:focus, 54 | button:focus-visible { 55 | outline: 4px auto -webkit-focus-ring-color; 56 | } 57 | 58 | @media (prefers-color-scheme: light) { 59 | :root { 60 | color: #213547; 61 | background-color: #ffffff; 62 | } 63 | a:hover { 64 | color: #747bff; 65 | } 66 | button { 67 | background-color: #f9f9f9; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /playground/backend-integration/src/main.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { createRoot } from 'react-dom/client' 3 | 4 | import App from './App' 5 | import './index.css' 6 | 7 | const container = document.getElementById('root') as HTMLElement 8 | const root = createRoot(container) 9 | root.render( 10 | 11 | 12 | 13 | ) 14 | -------------------------------------------------------------------------------- /playground/backend-integration/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /playground/backend-integration/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "useDefineForClassFields": true, 5 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 6 | "allowJs": false, 7 | "skipLibCheck": true, 8 | "esModuleInterop": false, 9 | "allowSyntheticDefaultImports": true, 10 | "strict": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "module": "ESNext", 13 | "moduleResolution": "Node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": true, 17 | "jsx": "react-jsx" 18 | }, 19 | "include": ["src"], 20 | "references": [{ "path": "./tsconfig.node.json" }] 21 | } 22 | -------------------------------------------------------------------------------- /playground/backend-integration/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "module": "ESNext", 5 | "moduleResolution": "Node", 6 | "allowSyntheticDefaultImports": true 7 | }, 8 | "include": ["vite.config.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /playground/backend-integration/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react' 3 | import checker from 'vite-plugin-checker' 4 | 5 | // https://vitejs.dev/config/ 6 | export default defineConfig({ 7 | build: { 8 | // generate manifest.json in outDir 9 | manifest: true, 10 | rollupOptions: { 11 | input: './src/main.tsx', 12 | }, 13 | }, 14 | plugins: [react(), checker({ typescript: true })], 15 | }) 16 | -------------------------------------------------------------------------------- /playground/biome-default/__tests__/__snapshots__/test.spec.ts.snap: -------------------------------------------------------------------------------- 1 | // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html 2 | 3 | exports[`biome > serve > get initial error and subsequent error 1`] = `"[{"checkerId":"Biome","frame":" 1 | const a = 'hello'/n > 2 | var b = 'world'/n | ^^^^^^^^^^^^^^^/n 3 | const c = '!'/n 4 | var d = a + b + c/n 5 |","id":"/playground-temp/biome-default/src/index.js","level":1,"loc":{"column":1,"file":"/playground-temp/biome-default/src/index.js","line":2},"message":"[lint/style/noVar] Use let or const instead of var.","stack":""},{"checkerId":"Biome","frame":" 2 | var b = 'world'/n 3 | const c = '!'/n > 4 | var d = a + b + c/n | ^^^^^^^^^^^^^^^^^/n 5 |","id":"/playground-temp/biome-default/src/index.js","level":1,"loc":{"column":1,"file":"/playground-temp/biome-default/src/index.js","line":4},"message":"[lint/style/noVar] Use let or const instead of var.","stack":""}]"`; 4 | 5 | exports[`biome > serve > get initial error and subsequent error 2`] = ` 6 | [ 7 | " ERROR(Biome) [lint/style/noVar] Use let or const instead of var. 8 | FILE /playground-temp/biome-default/src/index.js:2:1 9 | 10 | 1 | const a = 'hello' 11 | > 2 | var b = 'world' 12 | | ^^^^^^^^^^^^^^^ 13 | 3 | const c = '!' 14 | 4 | var d = a + b + c 15 | 5 | 16 | ", 17 | " ERROR(Biome) [lint/style/noVar] Use let or const instead of var. 18 | FILE /playground-temp/biome-default/src/index.js:4:1 19 | 20 | 2 | var b = 'world' 21 | 3 | const c = '!' 22 | > 4 | var d = a + b + c 23 | | ^^^^^^^^^^^^^^^^^ 24 | 5 | 25 | ", 26 | "[Biome] Found 2 errors and 0 warning", 27 | ] 28 | `; 29 | 30 | exports[`biome > serve > get initial error and subsequent error 3`] = `"[{"checkerId":"Biome","frame":" 2 | const b = 'world'/n 3 | const c = '!'/n > 4 | var d = a + b + c/n | ^^^^^^^^^^^^^^^^^/n 5 |","id":"/playground-temp/biome-default/src/index.js","level":1,"loc":{"column":1,"file":"/playground-temp/biome-default/src/index.js","line":4},"message":"[lint/style/noVar] Use let or const instead of var.","stack":""}]"`; 31 | 32 | exports[`biome > serve > get initial error and subsequent error 4`] = ` 33 | [ 34 | " ERROR(Biome) [lint/style/noVar] Use let or const instead of var. 35 | FILE /playground-temp/biome-default/src/index.js:4:1 36 | 37 | 2 | const b = 'world' 38 | 3 | const c = '!' 39 | > 4 | var d = a + b + c 40 | | ^^^^^^^^^^^^^^^^^ 41 | 5 | 42 | ", 43 | "[Biome] Found 1 error and 0 warning", 44 | ] 45 | `; 46 | -------------------------------------------------------------------------------- /playground/biome-default/__tests__/test.spec.ts: -------------------------------------------------------------------------------- 1 | import stringify from 'fast-json-stable-stringify' 2 | import fs from 'node:fs' 3 | import { describe, expect, it } from 'vitest' 4 | import { 5 | diagnostics, 6 | editFile, 7 | expectStderrContains, 8 | isBuild, 9 | isServe, 10 | stripedLog, 11 | resetDiagnostics, 12 | resetReceivedLog, 13 | sleepForEdit, 14 | sleepForServerReady, 15 | } from '../../testUtils' 16 | import path from 'node:path' 17 | 18 | describe('biome', () => { 19 | describe.runIf(isServe)('serve', () => { 20 | it('get initial error and subsequent error', async () => { 21 | await sleepForServerReady() 22 | expect(stringify(diagnostics)).toMatchSnapshot() 23 | expect(stripedLog).toMatchSnapshot() 24 | 25 | console.log('-- edit error file --') 26 | resetReceivedLog() 27 | resetDiagnostics() 28 | editFile('src/index.js', (code) => code.replace(`var b = 'world'`, `const b = 'world'`)) 29 | await sleepForEdit() 30 | expect(stringify(diagnostics)).toMatchSnapshot() 31 | expect(stripedLog).toMatchSnapshot() 32 | }) 33 | }) 34 | 35 | describe.runIf(isBuild)('build', () => { 36 | it('should fail', async () => { 37 | const expectedMsg = ['Use let or const instead of var', 'Found 2 errors'] 38 | expectStderrContains(stripedLog, expectedMsg) 39 | }) 40 | }) 41 | }) 42 | -------------------------------------------------------------------------------- /playground/biome-default/biome.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://biomejs.dev/schemas/1.8.3/schema.json", 3 | "files": { 4 | "include": ["./src/*.js"] 5 | }, 6 | "linter": { 7 | "rules": { 8 | "recommended": true 9 | } 10 | }, 11 | "formatter": { 12 | "enabled": false 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /playground/biome-default/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite App 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /playground/biome-default/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@playground/biome-default", 3 | "version": "0.0.0", 4 | "private": true, 5 | "type": "module", 6 | "scripts": { 7 | "build": "vite build", 8 | "dev": "vite", 9 | "lint": "biome lint", 10 | "serve": "vite preview" 11 | }, 12 | "devDependencies": { 13 | "@biomejs/biome": "^1.9.4", 14 | "vite": "^6.3.4", 15 | "vite-plugin-checker": "workspace:*" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /playground/biome-default/src/index.js: -------------------------------------------------------------------------------- 1 | const a = 'hello' 2 | var b = 'world' 3 | const c = '!' 4 | var d = a + b + c 5 | -------------------------------------------------------------------------------- /playground/biome-default/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 5 | "types": ["vite/client"], 6 | "allowJs": false, 7 | "skipLibCheck": false, 8 | "esModuleInterop": false, 9 | "allowSyntheticDefaultImports": true, 10 | "strict": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "module": "ESNext", 13 | "moduleResolution": "Node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": true 17 | }, 18 | "include": ["./src"] 19 | } 20 | -------------------------------------------------------------------------------- /playground/biome-default/vite.config.js: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | import { defineConfig } from 'vite' 3 | import checker from 'vite-plugin-checker' 4 | 5 | export default defineConfig({ 6 | plugins: [ 7 | checker({ 8 | biome: true, 9 | }), 10 | ], 11 | }) 12 | -------------------------------------------------------------------------------- /playground/config-default/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended"], 4 | "parser": "@typescript-eslint/parser", 5 | "plugins": ["@typescript-eslint"] 6 | } 7 | -------------------------------------------------------------------------------- /playground/config-default/__tests__/__snapshots__/test.spec.ts.snap: -------------------------------------------------------------------------------- 1 | // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html 2 | 3 | exports[`config-default > serve > get initial error 1`] = `"[{"checkerId":"ESLint","frame":" 1 | import { text } from './text'/n 2 |/n > 3 | var hello = 'Hello'/n | ^^^^^^^^^^^^^^^^^^^/n 4 |/n 5 | const rootDom = document.querySelector('#root')! as HTMLElement/n 6 | rootDom.innerHTML = hello + text","id":"/playground-temp/config-default/src/main.ts","level":1,"loc":{"column":1,"file":"/playground-temp/config-default/src/main.ts","line":3},"message":"Unexpected var, use let or const instead. (no-var)","stack":""},{"checkerId":"ESLint","frame":" 3 | var hello = 'Hello'/n 4 |/n > 5 | const rootDom = document.querySelector('#root')! as HTMLElement/n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^/n 6 | rootDom.innerHTML = hello + text/n 7 |/n 8 | export {}","id":"/playground-temp/config-default/src/main.ts","level":0,"loc":{"column":17,"file":"/playground-temp/config-default/src/main.ts","line":5},"message":"Forbidden non-null assertion. (@typescript-eslint/no-non-null-assertion)","stack":""}]"`; 4 | 5 | exports[`config-default > serve > get initial error 2`] = ` 6 | [ 7 | " ERROR(ESLint) Unexpected var, use let or const instead. (no-var) 8 | FILE /playground-temp/config-default/src/main.ts:3:1 9 | 10 | 1 | import { text } from './text' 11 | 2 | 12 | > 3 | var hello = 'Hello' 13 | | ^^^^^^^^^^^^^^^^^^^ 14 | 4 | 15 | 5 | const rootDom = document.querySelector('#root')! as HTMLElement 16 | 6 | rootDom.innerHTML = hello + text 17 | ", 18 | " WARNING(ESLint) Forbidden non-null assertion. (@typescript-eslint/no-non-null-assertion) 19 | FILE /playground-temp/config-default/src/main.ts:5:17 20 | 21 | 3 | var hello = 'Hello' 22 | 4 | 23 | > 5 | const rootDom = document.querySelector('#root')! as HTMLElement 24 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 25 | 6 | rootDom.innerHTML = hello + text 26 | 7 | 27 | 8 | export {} 28 | ", 29 | "[ESLint] Found 1 error and 1 warning", 30 | ] 31 | `; 32 | -------------------------------------------------------------------------------- /playground/config-default/__tests__/test.spec.ts: -------------------------------------------------------------------------------- 1 | import stringify from 'fast-json-stable-stringify' 2 | import { describe, expect, it } from 'vitest' 3 | 4 | import { 5 | diagnostics, 6 | expectStderrContains, 7 | isBuild, 8 | isServe, 9 | log, 10 | sleepForServerReady, 11 | stripedLog, 12 | } from '../../testUtils' 13 | 14 | describe('config-default', () => { 15 | describe.runIf(isServe)('serve', () => { 16 | it('get initial error', async () => { 17 | await sleepForServerReady() 18 | 19 | expect(stringify(diagnostics)).toMatchSnapshot() 20 | expect(stripedLog).toMatchSnapshot() 21 | }) 22 | }) 23 | 24 | describe.runIf(isBuild)('build', () => { 25 | it('should fail', async () => { 26 | const expectedMsg = 'Unexpected var, use let or const instead no-var' 27 | expectStderrContains(log, expectedMsg) 28 | }) 29 | }) 30 | }) 31 | -------------------------------------------------------------------------------- /playground/config-default/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite App 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /playground/config-default/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@playground/config-default", 3 | "version": "0.0.0", 4 | "private": true, 5 | "type": "module", 6 | "scripts": { 7 | "build": "vite build", 8 | "dev": "vite", 9 | "lint": "eslint --ext .js,.ts ./src/**", 10 | "serve": "vite preview" 11 | }, 12 | "devDependencies": { 13 | "@typescript-eslint/eslint-plugin": "^5.62.0", 14 | "@typescript-eslint/parser": "^5.62.0", 15 | "eslint": "^8.57.1", 16 | "typescript": "^5.8.3", 17 | "vite": "^6.3.4", 18 | "vite-plugin-checker": "workspace:*" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /playground/config-default/src/main.ts: -------------------------------------------------------------------------------- 1 | import { text } from './text' 2 | 3 | var hello = 'Hello' 4 | 5 | const rootDom = document.querySelector('#root')! as HTMLElement 6 | rootDom.innerHTML = hello + text 7 | 8 | export {} 9 | -------------------------------------------------------------------------------- /playground/config-default/src/text.ts: -------------------------------------------------------------------------------- 1 | export const text = 'Vanilla JS/TS' 2 | -------------------------------------------------------------------------------- /playground/config-default/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 5 | "types": ["vite/client"], 6 | "allowJs": false, 7 | "skipLibCheck": false, 8 | "esModuleInterop": false, 9 | "allowSyntheticDefaultImports": true, 10 | "strict": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "module": "ESNext", 13 | "moduleResolution": "Node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": true 17 | }, 18 | "include": ["./src"] 19 | } 20 | -------------------------------------------------------------------------------- /playground/config-default/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import checker from 'vite-plugin-checker' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | base: '/my-app/', 7 | // config-edit-slot 8 | plugins: [ 9 | checker({ 10 | eslint: { 11 | lintCommand: 'eslint ./src --ext .ts', 12 | }, 13 | // checker-edit-slot 14 | }), 15 | ], 16 | }) 17 | -------------------------------------------------------------------------------- /playground/config-enableBuild-false/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended"] 4 | } 5 | -------------------------------------------------------------------------------- /playground/config-enableBuild-false/__tests__/test.spec.ts: -------------------------------------------------------------------------------- 1 | import { describe, expect, it } from 'vitest' 2 | 3 | import { buildSucceed, isBuild } from '../../testUtils' 4 | 5 | describe('config-enableBuild-false', () => { 6 | describe.runIf(isBuild)('build', () => { 7 | it('should pass', async () => { 8 | expect(buildSucceed).toBe(true) 9 | }) 10 | }) 11 | }) 12 | -------------------------------------------------------------------------------- /playground/config-enableBuild-false/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite App 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /playground/config-enableBuild-false/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@playground/config-enable-build-false", 3 | "version": "0.0.0", 4 | "private": true, 5 | "type": "module", 6 | "scripts": { 7 | "build": "vite build", 8 | "dev": "vite", 9 | "lint": "eslint --ext .js,.ts ./src/**", 10 | "serve": "vite preview" 11 | }, 12 | "devDependencies": { 13 | "@typescript-eslint/eslint-plugin": "^5.62.0", 14 | "@typescript-eslint/parser": "^5.62.0", 15 | "eslint": "^8.57.1", 16 | "typescript": "^5.8.3", 17 | "vite": "^6.3.4", 18 | "vite-plugin-checker": "workspace:*" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /playground/config-enableBuild-false/src/main.ts: -------------------------------------------------------------------------------- 1 | import { text } from './text' 2 | 3 | var hello = 'Hello' 4 | 5 | const rootDom = document.querySelector('#root')! as HTMLElement 6 | rootDom.innerHTML = hello + text 7 | 8 | export {} 9 | -------------------------------------------------------------------------------- /playground/config-enableBuild-false/src/text.ts: -------------------------------------------------------------------------------- 1 | export const text = 'Vanilla JS/TS' 2 | -------------------------------------------------------------------------------- /playground/config-enableBuild-false/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 5 | "types": ["vite/client"], 6 | "allowJs": false, 7 | "skipLibCheck": false, 8 | "esModuleInterop": false, 9 | "allowSyntheticDefaultImports": true, 10 | "strict": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "module": "ESNext", 13 | "moduleResolution": "Node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": true 17 | }, 18 | "include": ["./src"] 19 | } 20 | -------------------------------------------------------------------------------- /playground/config-enableBuild-false/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import checker from 'vite-plugin-checker' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | // config-edit-slot 7 | plugins: [ 8 | checker({ 9 | enableBuild: false, 10 | eslint: { 11 | lintCommand: 'eslint ./src --ext .ts', 12 | }, 13 | // checker-edit-slot 14 | }), 15 | ], 16 | }) 17 | -------------------------------------------------------------------------------- /playground/config-initialIsOpen-error-warnings/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended"] 4 | } 5 | -------------------------------------------------------------------------------- /playground/config-initialIsOpen-error-warnings/__tests__/__snapshots__/test.spec.ts.snap: -------------------------------------------------------------------------------- 1 | // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html 2 | 3 | exports[`config-initialIsOpen-error-warnings > serve > should not show overlay when only warning exists 1`] = ` 4 | " 1 | import { text } from './text' 5 | 2 | 6 | > 3 | var rootDom = document.querySelector('#root')! as HTMLElement 7 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 8 | 4 | rootDom.innerHTML = text 9 | 5 | 10 | 6 | export {}" 11 | `; 12 | -------------------------------------------------------------------------------- /playground/config-initialIsOpen-error-warnings/__tests__/test.spec.ts: -------------------------------------------------------------------------------- 1 | import { describe, expect, it } from 'vitest' 2 | 3 | import { 4 | editFile, 5 | getHmrOverlay, 6 | getHmrOverlayText, 7 | isServe, 8 | pollingUntil, 9 | sleepForEdit, 10 | sleepForServerReady, 11 | } from '../../testUtils' 12 | 13 | describe('config-initialIsOpen-error-warnings', () => { 14 | describe.runIf(isServe)('serve', () => { 15 | it('should not show overlay when only warning exists', async () => { 16 | await sleepForServerReady() 17 | try { 18 | await getHmrOverlayText() 19 | } catch (e) { 20 | expect((e as any).toString()).toContain( 21 | 'Invariant failed: shadow dom is expected to be found, but got null' 22 | ) 23 | } 24 | 25 | console.log('-- overlay appears after introduce an error --') 26 | editFile('src/main.ts', (code) => code.replace('const rootDom', 'var rootDom')) 27 | await sleepForEdit() 28 | await getHmrOverlayText() 29 | await pollingUntil(getHmrOverlay, (dom) => !!dom) 30 | const [, , frame] = await getHmrOverlayText() 31 | expect(frame).toMatchSnapshot() 32 | }) 33 | }) 34 | }) 35 | -------------------------------------------------------------------------------- /playground/config-initialIsOpen-error-warnings/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite App 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /playground/config-initialIsOpen-error-warnings/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@playground/config-initial-is-open-error-warnings", 3 | "version": "0.0.0", 4 | "private": true, 5 | "type": "module", 6 | "scripts": { 7 | "build": "vite build", 8 | "dev": "vite", 9 | "lint": "eslint --ext .js,.ts ./src/**", 10 | "serve": "vite preview" 11 | }, 12 | "devDependencies": { 13 | "@typescript-eslint/eslint-plugin": "^5.62.0", 14 | "@typescript-eslint/parser": "^5.62.0", 15 | "eslint": "^8.57.1", 16 | "typescript": "^5.8.3", 17 | "vite": "^6.3.4", 18 | "vite-plugin-checker": "workspace:*" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /playground/config-initialIsOpen-error-warnings/src/main.ts: -------------------------------------------------------------------------------- 1 | import { text } from './text' 2 | 3 | const rootDom = document.querySelector('#root')! as HTMLElement 4 | rootDom.innerHTML = text 5 | 6 | export {} 7 | -------------------------------------------------------------------------------- /playground/config-initialIsOpen-error-warnings/src/text.ts: -------------------------------------------------------------------------------- 1 | export const text = 'Vanilla JS/TS' 2 | -------------------------------------------------------------------------------- /playground/config-initialIsOpen-error-warnings/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 5 | "types": ["vite/client"], 6 | "allowJs": false, 7 | "skipLibCheck": false, 8 | "esModuleInterop": false, 9 | "allowSyntheticDefaultImports": true, 10 | "strict": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "module": "ESNext", 13 | "moduleResolution": "Node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": true 17 | }, 18 | "include": ["./src"] 19 | } 20 | -------------------------------------------------------------------------------- /playground/config-initialIsOpen-error-warnings/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import checker from 'vite-plugin-checker' 3 | 4 | export default defineConfig({ 5 | plugins: [ 6 | checker({ 7 | overlay: { 8 | initialIsOpen: 'error', 9 | }, 10 | eslint: { 11 | lintCommand: 'eslint ./src --ext .ts', 12 | }, 13 | }), 14 | ], 15 | }) 16 | -------------------------------------------------------------------------------- /playground/config-initialIsOpen-error/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended"] 4 | } 5 | -------------------------------------------------------------------------------- /playground/config-initialIsOpen-error/__tests__/__snapshots__/test.spec.ts.snap: -------------------------------------------------------------------------------- 1 | // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html 2 | 3 | exports[`config-initialIsOpen-error > serve > -- should find overlay with error -- 1`] = `"Unexpected var, use let or const instead. (no-var)"`; 4 | 5 | exports[`config-initialIsOpen-error > serve > -- should find overlay with error -- 2`] = `"/playground-temp/config-initialIsOpen-error/src/main.ts:3:1"`; 6 | 7 | exports[`config-initialIsOpen-error > serve > -- should find overlay with error -- 3`] = ` 8 | " 1 | import { text } from './text' 9 | 2 | 10 | > 3 | var hello = 'Hello' 11 | | ^^^^^^^^^^^^^^^^^^^ 12 | 4 | 13 | 5 | const rootDom = document.querySelector('#root')! as HTMLElement 14 | 6 | rootDom.innerHTML = hello + text" 15 | `; 16 | 17 | exports[`config-initialIsOpen-error > serve > -- should find overlay with error -- 4`] = `""`; 18 | -------------------------------------------------------------------------------- /playground/config-initialIsOpen-error/__tests__/test.spec.ts: -------------------------------------------------------------------------------- 1 | import { describe, expect, it } from 'vitest' 2 | 3 | import { 4 | editFile, 5 | getHmrOverlay, 6 | getHmrOverlayText, 7 | isServe, 8 | pollingUntil, 9 | sleep, 10 | sleepForEdit, 11 | sleepForServerReady, 12 | } from '../../testUtils' 13 | 14 | describe('config-initialIsOpen-error', () => { 15 | describe.runIf(isServe)('serve', () => { 16 | it('-- should find overlay with error --', async () => { 17 | await sleepForServerReady() 18 | const [message1, file1, frame1] = await getHmrOverlayText() 19 | expect(message1).toMatchSnapshot() 20 | expect(file1).toMatchSnapshot() 21 | expect(frame1).toMatchSnapshot() 22 | 23 | console.log('-- overlay remains after fix error --') 24 | editFile('src/main.ts', (code) => code.replace('var hello', `const hello`)) 25 | await sleepForEdit() 26 | await pollingUntil(getHmrOverlay, (dom) => !!dom) 27 | const [, , frame2] = await getHmrOverlayText() 28 | expect(frame2).toMatchSnapshot() 29 | 30 | console.log('-- overlay dismiss after fix warning --') 31 | editFile('src/main.ts', (code) => code.replace('! as', ` as`)) 32 | await sleep(6000) 33 | await expect(getHmrOverlayText()).rejects.toThrow( 34 | 'Invariant failed: .message-body is expected in shadow root' 35 | ) 36 | }) 37 | }) 38 | }) 39 | -------------------------------------------------------------------------------- /playground/config-initialIsOpen-error/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite App 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /playground/config-initialIsOpen-error/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@playground/config-initial-is-open-error", 3 | "version": "0.0.0", 4 | "private": true, 5 | "type": "module", 6 | "scripts": { 7 | "build": "vite build", 8 | "dev": "vite", 9 | "lint": "eslint --ext .js,.ts ./src/**", 10 | "serve": "vite preview" 11 | }, 12 | "devDependencies": { 13 | "@typescript-eslint/eslint-plugin": "^5.62.0", 14 | "@typescript-eslint/parser": "^5.62.0", 15 | "eslint": "^8.57.1", 16 | "typescript": "^5.8.3", 17 | "vite": "^6.3.4", 18 | "vite-plugin-checker": "workspace:*" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /playground/config-initialIsOpen-error/src/main.ts: -------------------------------------------------------------------------------- 1 | import { text } from './text' 2 | 3 | var hello = 'Hello' 4 | 5 | const rootDom = document.querySelector('#root')! as HTMLElement 6 | rootDom.innerHTML = hello + text 7 | 8 | export {} 9 | -------------------------------------------------------------------------------- /playground/config-initialIsOpen-error/src/text.ts: -------------------------------------------------------------------------------- 1 | export const text = 'Vanilla JS/TS' 2 | -------------------------------------------------------------------------------- /playground/config-initialIsOpen-error/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 5 | "types": ["vite/client"], 6 | "allowJs": false, 7 | "skipLibCheck": false, 8 | "esModuleInterop": false, 9 | "allowSyntheticDefaultImports": true, 10 | "strict": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "module": "ESNext", 13 | "moduleResolution": "Node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": true 17 | }, 18 | "include": ["./src"] 19 | } 20 | -------------------------------------------------------------------------------- /playground/config-initialIsOpen-error/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import checker from 'vite-plugin-checker' 3 | 4 | export default defineConfig({ 5 | plugins: [ 6 | checker({ 7 | overlay: { 8 | initialIsOpen: 'error', 9 | }, 10 | eslint: { 11 | lintCommand: 'eslint ./src --ext .ts', 12 | }, 13 | }), 14 | ], 15 | }) 16 | -------------------------------------------------------------------------------- /playground/config-initialIsOpen-false/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended"] 4 | } 5 | -------------------------------------------------------------------------------- /playground/config-initialIsOpen-false/__tests__/test.spec.ts: -------------------------------------------------------------------------------- 1 | import { describe, expect, it } from 'vitest' 2 | 3 | import { getHmrOverlayText, isServe, sleepForServerReady } from '../../testUtils' 4 | 5 | describe('config-initialIsOpen-false', () => { 6 | describe.runIf(isServe)('serve', () => { 7 | it('should not find overlay', async () => { 8 | if (isServe) { 9 | await sleepForServerReady() 10 | try { 11 | await getHmrOverlayText() 12 | } catch (e) { 13 | expect((e as any).toString()).toContain( 14 | 'Invariant failed: shadow dom is expected to be found, but got null' 15 | ) 16 | } 17 | } 18 | }) 19 | }) 20 | }) 21 | -------------------------------------------------------------------------------- /playground/config-initialIsOpen-false/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite App 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /playground/config-initialIsOpen-false/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@playground/config-initial-is-open-false", 3 | "version": "0.0.0", 4 | "private": true, 5 | "type": "module", 6 | "scripts": { 7 | "build": "vite build", 8 | "dev": "vite", 9 | "lint": "eslint --ext .js,.ts ./src/**", 10 | "serve": "vite preview" 11 | }, 12 | "devDependencies": { 13 | "@typescript-eslint/eslint-plugin": "^5.62.0", 14 | "@typescript-eslint/parser": "^5.62.0", 15 | "eslint": "^8.57.1", 16 | "typescript": "^5.8.3", 17 | "vite": "^6.3.4", 18 | "vite-plugin-checker": "workspace:*" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /playground/config-initialIsOpen-false/src/main.ts: -------------------------------------------------------------------------------- 1 | import { text } from './text' 2 | 3 | var hello = 'Hello' 4 | 5 | const rootDom = document.querySelector('#root')! as HTMLElement 6 | rootDom.innerHTML = hello + text 7 | 8 | export {} 9 | -------------------------------------------------------------------------------- /playground/config-initialIsOpen-false/src/text.ts: -------------------------------------------------------------------------------- 1 | export const text = 'Vanilla JS/TS' 2 | -------------------------------------------------------------------------------- /playground/config-initialIsOpen-false/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 5 | "types": ["vite/client"], 6 | "allowJs": false, 7 | "skipLibCheck": false, 8 | "esModuleInterop": false, 9 | "allowSyntheticDefaultImports": true, 10 | "strict": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "module": "ESNext", 13 | "moduleResolution": "Node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": true 17 | }, 18 | "include": ["./src"] 19 | } 20 | -------------------------------------------------------------------------------- /playground/config-initialIsOpen-false/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import checker from 'vite-plugin-checker' 3 | 4 | export default defineConfig({ 5 | plugins: [ 6 | checker({ 7 | overlay: { 8 | initialIsOpen: false, 9 | }, 10 | eslint: { 11 | lintCommand: 'eslint ./src --ext .ts', 12 | }, 13 | }), 14 | ], 15 | }) 16 | -------------------------------------------------------------------------------- /playground/config-no-runtime-in-build/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended"] 4 | } 5 | -------------------------------------------------------------------------------- /playground/config-no-runtime-in-build/__tests__/test.spec.ts: -------------------------------------------------------------------------------- 1 | import fs from 'fs' 2 | import { globSync } from 'tinyglobby' 3 | import path from 'path' 4 | import { describe, expect, it } from 'vitest' 5 | 6 | import { isBuild, sleepForServerReady, testDir } from '../../testUtils' 7 | 8 | describe('config-no-runtime-code-in-build', () => { 9 | describe.runIf(isBuild)('build', () => { 10 | it('should not contain plugin code in build artifacts', async () => { 11 | await sleepForServerReady() 12 | const files = globSync('**', { cwd: path.resolve(testDir, 'dist'), absolute: true, onlyFiles: true }) 13 | expect(files.length).toBeGreaterThan(1) 14 | for await (const file of files) { 15 | const content = await fs.promises.readFile(file, 'utf-8') 16 | expect(content).not.toContain('vite-plugin-checker') 17 | } 18 | }) 19 | }) 20 | }) 21 | -------------------------------------------------------------------------------- /playground/config-no-runtime-in-build/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite App 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /playground/config-no-runtime-in-build/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@playground/config-no-runtime-in-build", 3 | "version": "0.0.0", 4 | "private": true, 5 | "type": "module", 6 | "scripts": { 7 | "build": "vite build", 8 | "dev": "vite", 9 | "lint": "eslint --ext .js,.ts ./src/**", 10 | "serve": "vite preview" 11 | }, 12 | "devDependencies": { 13 | "@typescript-eslint/eslint-plugin": "^5.62.0", 14 | "@typescript-eslint/parser": "^5.62.0", 15 | "eslint": "^8.57.1", 16 | "typescript": "^5.8.3", 17 | "vite": "^6.3.4", 18 | "vite-plugin-checker": "workspace:*" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /playground/config-no-runtime-in-build/src/main.ts: -------------------------------------------------------------------------------- 1 | import { text } from './text' 2 | 3 | console.log(text) 4 | -------------------------------------------------------------------------------- /playground/config-no-runtime-in-build/src/text.ts: -------------------------------------------------------------------------------- 1 | export const text = 'Vanilla JS/TS' 2 | -------------------------------------------------------------------------------- /playground/config-no-runtime-in-build/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 5 | "types": ["vite/client"], 6 | "allowJs": false, 7 | "skipLibCheck": false, 8 | "esModuleInterop": false, 9 | "allowSyntheticDefaultImports": true, 10 | "strict": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "module": "ESNext", 13 | "moduleResolution": "Node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": true 17 | }, 18 | "include": ["./src"] 19 | } 20 | -------------------------------------------------------------------------------- /playground/config-no-runtime-in-build/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import checker from 'vite-plugin-checker' 3 | 4 | export default defineConfig({ 5 | plugins: [ 6 | checker({ 7 | eslint: { 8 | lintCommand: 'eslint ./src --ext .ts', 9 | }, 10 | }), 11 | ], 12 | }) 13 | -------------------------------------------------------------------------------- /playground/config-overlay-changes/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended"] 4 | } 5 | -------------------------------------------------------------------------------- /playground/config-overlay-changes/__tests__/__snapshots__/test.spec.ts.snap: -------------------------------------------------------------------------------- 1 | // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html 2 | 3 | exports[`config-overlay-changes > serve > get initial error from overlay and overlay error content changes on modifying 1`] = `"Unexpected var, use let or const instead. (no-var)"`; 4 | 5 | exports[`config-overlay-changes > serve > get initial error from overlay and overlay error content changes on modifying 2`] = `"/playground-temp/config-overlay-changes/src/main.ts:3:1"`; 6 | 7 | exports[`config-overlay-changes > serve > get initial error from overlay and overlay error content changes on modifying 3`] = ` 8 | " 1 | import { text } from './text' 9 | 2 | 10 | > 3 | var hello = 'Hello' 11 | | ^^^^^^^^^^^^^^^^^^^ 12 | 4 | 13 | 5 | const rootDom = document.querySelector('#root')! as HTMLElement 14 | 6 | rootDom.innerHTML = hello + text" 15 | `; 16 | 17 | exports[`config-overlay-changes > serve > get initial error from overlay and overlay error content changes on modifying 4`] = ` 18 | " 1 | import { text } from './text' 19 | 2 | 20 | > 3 | var hello = 'Hello1' 21 | | ^^^^^^^^^^^^^^^^^^^^ 22 | 4 | 23 | 5 | const rootDom = document.querySelector('#root')! as HTMLElement 24 | 6 | rootDom.innerHTML = hello + text" 25 | `; 26 | -------------------------------------------------------------------------------- /playground/config-overlay-changes/__tests__/test.spec.ts: -------------------------------------------------------------------------------- 1 | import { describe, expect, it } from 'vitest' 2 | 3 | import { 4 | editFile, 5 | getHmrOverlay, 6 | getHmrOverlayText, 7 | isServe, 8 | pollingUntil, 9 | sleep, 10 | sleepForEdit, 11 | sleepForServerReady, 12 | } from '../../testUtils' 13 | 14 | describe('config-overlay-changes', () => { 15 | describe.runIf(isServe)('serve', () => { 16 | it('get initial error from overlay and overlay error content changes on modifying', async () => { 17 | await sleepForServerReady() 18 | const [message1, file1, frame1] = await getHmrOverlayText() 19 | expect(message1).toMatchSnapshot() 20 | expect(file1).toMatchSnapshot() 21 | expect(frame1).toMatchSnapshot() 22 | 23 | console.log('-- overlay update after edit --') 24 | editFile('src/main.ts', (code) => code.replace('Hello', 'Hello1')) 25 | await sleepForEdit() 26 | await pollingUntil(getHmrOverlay, (dom) => !!dom) 27 | const [, , frame2] = await getHmrOverlayText() 28 | expect(frame2).toMatchSnapshot() 29 | 30 | console.log('-- overlay dismiss after fix error --') 31 | editFile('src/main.ts', (code) => code.replace('var hello', `const hello`)) 32 | editFile('src/main.ts', (code) => code.replace('! as', ` as`)) 33 | await sleep(6000) 34 | await expect(getHmrOverlayText()).rejects.toThrow( 35 | 'Invariant failed: .message-body is expected in shadow root' 36 | ) 37 | }) 38 | }) 39 | }) 40 | -------------------------------------------------------------------------------- /playground/config-overlay-changes/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite App 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /playground/config-overlay-changes/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@playground/config-overlay-changes", 3 | "version": "0.0.0", 4 | "private": true, 5 | "type": "module", 6 | "scripts": { 7 | "build": "vite build", 8 | "dev": "vite", 9 | "lint": "eslint --ext .js,.ts ./src/**", 10 | "serve": "vite preview" 11 | }, 12 | "devDependencies": { 13 | "@typescript-eslint/eslint-plugin": "^5.62.0", 14 | "@typescript-eslint/parser": "^5.62.0", 15 | "eslint": "^8.57.1", 16 | "typescript": "^5.8.3", 17 | "vite": "^6.3.4", 18 | "vite-plugin-checker": "workspace:*" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /playground/config-overlay-changes/src/main.ts: -------------------------------------------------------------------------------- 1 | import { text } from './text' 2 | 3 | var hello = 'Hello' 4 | 5 | const rootDom = document.querySelector('#root')! as HTMLElement 6 | rootDom.innerHTML = hello + text 7 | 8 | export {} 9 | -------------------------------------------------------------------------------- /playground/config-overlay-changes/src/text.ts: -------------------------------------------------------------------------------- 1 | export const text = 'Vanilla JS/TS' 2 | -------------------------------------------------------------------------------- /playground/config-overlay-changes/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 5 | "types": ["vite/client"], 6 | "allowJs": false, 7 | "skipLibCheck": false, 8 | "esModuleInterop": false, 9 | "allowSyntheticDefaultImports": true, 10 | "strict": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "module": "ESNext", 13 | "moduleResolution": "Node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": true 17 | }, 18 | "include": ["./src"] 19 | } 20 | -------------------------------------------------------------------------------- /playground/config-overlay-changes/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import checker from 'vite-plugin-checker' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | // config-edit-slot 7 | plugins: [ 8 | checker({ 9 | eslint: { 10 | lintCommand: 'eslint ./src --ext .ts', 11 | }, 12 | // checker-edit-slot 13 | }), 14 | ], 15 | }) 16 | -------------------------------------------------------------------------------- /playground/config-overlay-false/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended"] 4 | } 5 | -------------------------------------------------------------------------------- /playground/config-overlay-false/__tests__/test.spec.ts: -------------------------------------------------------------------------------- 1 | import { describe, expect, it } from 'vitest' 2 | 3 | import { getHmrOverlayText, isServe, sleepForServerReady } from '../../testUtils' 4 | 5 | describe('config-overlay-false', () => { 6 | describe.runIf(isServe)('serve', async () => { 7 | it('should not find overlay', async () => { 8 | await sleepForServerReady() 9 | try { 10 | await getHmrOverlayText() 11 | } catch (e) { 12 | await expect((e as any).toString()).toContain( 13 | 'Error: Invariant failed: .message-body is expected in shadow root' 14 | ) 15 | } 16 | }) 17 | }) 18 | }) 19 | -------------------------------------------------------------------------------- /playground/config-overlay-false/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite App 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /playground/config-overlay-false/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@playground/config-overlay-false", 3 | "version": "0.0.0", 4 | "private": true, 5 | "type": "module", 6 | "scripts": { 7 | "build": "vite build", 8 | "dev": "vite", 9 | "lint": "eslint --ext .js,.ts ./src/**", 10 | "serve": "vite preview" 11 | }, 12 | "devDependencies": { 13 | "@typescript-eslint/eslint-plugin": "^5.62.0", 14 | "@typescript-eslint/parser": "^5.62.0", 15 | "eslint": "^8.57.1", 16 | "typescript": "^5.8.3", 17 | "vite": "^6.3.4", 18 | "vite-plugin-checker": "workspace:*" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /playground/config-overlay-false/src/main.ts: -------------------------------------------------------------------------------- 1 | import { text } from './text' 2 | 3 | var hello = 'Hello' 4 | 5 | const rootDom = document.querySelector('#root')! as HTMLElement 6 | rootDom.innerHTML = hello + text 7 | 8 | export {} 9 | -------------------------------------------------------------------------------- /playground/config-overlay-false/src/text.ts: -------------------------------------------------------------------------------- 1 | export const text = 'Vanilla JS/TS' 2 | -------------------------------------------------------------------------------- /playground/config-overlay-false/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 5 | "types": ["vite/client"], 6 | "allowJs": false, 7 | "skipLibCheck": false, 8 | "esModuleInterop": false, 9 | "allowSyntheticDefaultImports": true, 10 | "strict": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "module": "ESNext", 13 | "moduleResolution": "Node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": true 17 | }, 18 | "include": ["./src"] 19 | } 20 | -------------------------------------------------------------------------------- /playground/config-overlay-false/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import checker from 'vite-plugin-checker' 3 | 4 | 5 | export default defineConfig({ 6 | plugins: [ 7 | checker({ 8 | overlay: false, 9 | eslint: { 10 | lintCommand: 'eslint ./src --ext .ts', 11 | }, 12 | }), 13 | ], 14 | }) 15 | -------------------------------------------------------------------------------- /playground/config-overlay-position-style/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended"] 4 | } 5 | -------------------------------------------------------------------------------- /playground/config-overlay-position-style/__tests__/test.spec.ts: -------------------------------------------------------------------------------- 1 | import { sleepForServerReady, getHmrOverlay, isServe } from '../../testUtils' 2 | import { describe, expect, it } from 'vitest' 3 | 4 | describe('config-overlay-position-style', () => { 5 | describe.runIf(isServe)('serve', () => { 6 | it('find badge in right top corner and customized by badgeStyle', async () => { 7 | await sleepForServerReady() 8 | const shadowRoot = await getHmrOverlay() 9 | const badge = await shadowRoot!.$('.badge-base') 10 | 11 | const { bgColor, top, right } = await badge!.evaluate((el) => { 12 | const bgColor = window.getComputedStyle(el).getPropertyValue('background-color') 13 | const top = window.getComputedStyle(el).getPropertyValue('top') 14 | const right = window.getComputedStyle(el).getPropertyValue('right') 15 | return { bgColor, top, right } 16 | }) 17 | 18 | expect(bgColor).toBe('rgb(231, 153, 176)') 19 | expect(top).toBe('0px') 20 | expect(right).toBe('0px') 21 | }) 22 | 23 | it('panel be customized by panelStyle', async () => { 24 | await sleepForServerReady() 25 | const shadowRoot = await getHmrOverlay() 26 | const panel = await shadowRoot!.$('.window') 27 | 28 | const { bgColor } = await panel!.evaluate((el) => { 29 | const bgColor = window.getComputedStyle(el).getPropertyValue('background-color') 30 | return { bgColor } 31 | }) 32 | 33 | expect(bgColor).toBe('rgb(164, 193, 255)') 34 | }) 35 | }) 36 | }) 37 | -------------------------------------------------------------------------------- /playground/config-overlay-position-style/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite App 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /playground/config-overlay-position-style/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@playground/config-overlay-position-style", 3 | "version": "0.0.0", 4 | "private": true, 5 | "type": "module", 6 | "scripts": { 7 | "build": "vite build", 8 | "dev": "vite", 9 | "lint": "eslint --ext .js,.ts ./src/**", 10 | "serve": "vite preview" 11 | }, 12 | "devDependencies": { 13 | "@typescript-eslint/eslint-plugin": "^5.62.0", 14 | "@typescript-eslint/parser": "^5.62.0", 15 | "eslint": "^8.57.1", 16 | "typescript": "^5.8.3", 17 | "vite": "^6.3.4", 18 | "vite-plugin-checker": "workspace:*" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /playground/config-overlay-position-style/src/main.ts: -------------------------------------------------------------------------------- 1 | import { text } from './text' 2 | 3 | var hello = 'Hello' 4 | 5 | const rootDom = document.querySelector('#root')! as HTMLElement 6 | rootDom.innerHTML = hello + text 7 | 8 | export {} 9 | -------------------------------------------------------------------------------- /playground/config-overlay-position-style/src/text.ts: -------------------------------------------------------------------------------- 1 | export const text = 'Vanilla JS/TS' 2 | -------------------------------------------------------------------------------- /playground/config-overlay-position-style/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 5 | "types": ["vite/client"], 6 | "allowJs": false, 7 | "skipLibCheck": false, 8 | "esModuleInterop": false, 9 | "allowSyntheticDefaultImports": true, 10 | "strict": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "module": "ESNext", 13 | "moduleResolution": "Node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": true 17 | }, 18 | "include": ["./src"] 19 | } 20 | -------------------------------------------------------------------------------- /playground/config-overlay-position-style/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import checker from 'vite-plugin-checker' 3 | 4 | export default defineConfig({ 5 | plugins: [ 6 | checker({ 7 | overlay: { 8 | position: 'tr', 9 | badgeStyle: 'background-color: #E799B0', 10 | panelStyle: 'background-color: #A4C1FF;', 11 | }, 12 | eslint: { 13 | lintCommand: 'eslint ./src --ext .ts', 14 | }, 15 | }), 16 | ], 17 | }) 18 | -------------------------------------------------------------------------------- /playground/config-terminal-false/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended"] 4 | } 5 | -------------------------------------------------------------------------------- /playground/config-terminal-false/__tests__/__snapshots__/test.spec.ts.snap: -------------------------------------------------------------------------------- 1 | // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html 2 | 3 | exports[`config-terminal-false > serve > should not log into terminal 1`] = `"[{"checkerId":"ESLint","frame":" 1 | import { text } from './text'/n 2 |/n > 3 | var hello = 'Hello'/n | ^^^^^^^^^^^^^^^^^^^/n 4 |/n 5 | const rootDom = document.querySelector('#root')! as HTMLElement/n 6 | rootDom.innerHTML = hello + text","id":"/playground-temp/config-terminal-false/src/main.ts","level":1,"loc":{"column":1,"file":"/playground-temp/config-terminal-false/src/main.ts","line":3},"message":"Unexpected var, use let or const instead. (no-var)","stack":""},{"checkerId":"ESLint","frame":" 3 | var hello = 'Hello'/n 4 |/n > 5 | const rootDom = document.querySelector('#root')! as HTMLElement/n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^/n 6 | rootDom.innerHTML = hello + text/n 7 |/n 8 | export {}","id":"/playground-temp/config-terminal-false/src/main.ts","level":0,"loc":{"column":17,"file":"/playground-temp/config-terminal-false/src/main.ts","line":5},"message":"Forbidden non-null assertion. (@typescript-eslint/no-non-null-assertion)","stack":""}]"`; 4 | 5 | exports[`config-terminal-false > serve > should not log into terminal 2`] = `[]`; 6 | -------------------------------------------------------------------------------- /playground/config-terminal-false/__tests__/test.spec.ts: -------------------------------------------------------------------------------- 1 | import stringify from 'fast-json-stable-stringify' 2 | import { describe, expect, it } from 'vitest' 3 | 4 | import { diagnostics, isServe, sleepForServerReady, stripedLog } from '../../testUtils' 5 | 6 | describe('config-terminal-false', () => { 7 | describe.runIf(isServe)('serve', () => { 8 | it('should not log into terminal', async () => { 9 | await sleepForServerReady() 10 | expect(stringify(diagnostics)).toMatchSnapshot() 11 | expect(stripedLog).toMatchSnapshot() 12 | }) 13 | }) 14 | }) 15 | -------------------------------------------------------------------------------- /playground/config-terminal-false/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite App 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /playground/config-terminal-false/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@playground/config-terminal-false", 3 | "version": "0.0.0", 4 | "private": true, 5 | "type": "module", 6 | "scripts": { 7 | "build": "vite build", 8 | "dev": "vite", 9 | "lint": "eslint --ext .js,.ts ./src/**", 10 | "serve": "vite preview" 11 | }, 12 | "devDependencies": { 13 | "@typescript-eslint/eslint-plugin": "^5.62.0", 14 | "@typescript-eslint/parser": "^5.62.0", 15 | "eslint": "^8.57.1", 16 | "typescript": "^5.8.3", 17 | "vite": "^6.3.4", 18 | "vite-plugin-checker": "workspace:*" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /playground/config-terminal-false/src/main.ts: -------------------------------------------------------------------------------- 1 | import { text } from './text' 2 | 3 | var hello = 'Hello' 4 | 5 | const rootDom = document.querySelector('#root')! as HTMLElement 6 | rootDom.innerHTML = hello + text 7 | 8 | export {} 9 | -------------------------------------------------------------------------------- /playground/config-terminal-false/src/text.ts: -------------------------------------------------------------------------------- 1 | export const text = 'Vanilla JS/TS' 2 | -------------------------------------------------------------------------------- /playground/config-terminal-false/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 5 | "types": ["vite/client"], 6 | "allowJs": false, 7 | "skipLibCheck": false, 8 | "esModuleInterop": false, 9 | "allowSyntheticDefaultImports": true, 10 | "strict": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "module": "ESNext", 13 | "moduleResolution": "Node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": true 17 | }, 18 | "include": ["./src"] 19 | } 20 | -------------------------------------------------------------------------------- /playground/config-terminal-false/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import checker from 'vite-plugin-checker' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | // config-edit-slot 7 | plugins: [ 8 | checker({ 9 | terminal: false, 10 | eslint: { 11 | lintCommand: 'eslint ./src --ext .ts', 12 | }, 13 | // checker-edit-slot 14 | }), 15 | ], 16 | }) 17 | -------------------------------------------------------------------------------- /playground/eslint-config-log-level/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended"] 4 | } 5 | -------------------------------------------------------------------------------- /playground/eslint-config-log-level/__tests__/test.spec.ts: -------------------------------------------------------------------------------- 1 | import stringify from 'fast-json-stable-stringify' 2 | import { describe, expect, it } from 'vitest' 3 | 4 | import { 5 | diagnostics, 6 | editFile, 7 | isServe, 8 | resetReceivedLog, 9 | sleepForEdit, 10 | sleepForServerReady, 11 | stripedLog, 12 | } from '../../testUtils' 13 | 14 | describe('eslint-config-log-level', () => { 15 | describe.runIf(isServe)('serve', () => { 16 | it('should only emit warning logs', async () => { 17 | await sleepForServerReady() 18 | expect(stringify(diagnostics)).toMatchSnapshot() 19 | expect(stripedLog).toMatchSnapshot() 20 | 21 | console.log('-- edit error file --') 22 | resetReceivedLog() 23 | editFile('src/main.ts', (code) => code.replace(`'Hello'`, `'Hello~'`)) 24 | await sleepForEdit() 25 | expect(stringify(diagnostics)).toMatchSnapshot() 26 | expect(stripedLog).toMatchSnapshot() 27 | }) 28 | }) 29 | }) 30 | -------------------------------------------------------------------------------- /playground/eslint-config-log-level/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite App 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /playground/eslint-config-log-level/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@playground/eslint-config-log-level", 3 | "version": "0.0.0", 4 | "private": true, 5 | "type": "module", 6 | "scripts": { 7 | "build": "vite build", 8 | "dev": "vite", 9 | "lint": "eslint --ext .js,.ts ./src/**", 10 | "serve": "vite preview" 11 | }, 12 | "devDependencies": { 13 | "@typescript-eslint/eslint-plugin": "^5.62.0", 14 | "@typescript-eslint/parser": "^5.62.0", 15 | "eslint": "^8.57.1", 16 | "typescript": "^5.8.3", 17 | "vite": "^6.3.4", 18 | "vite-plugin-checker": "workspace:*" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /playground/eslint-config-log-level/src/main.ts: -------------------------------------------------------------------------------- 1 | import { text } from './text' 2 | 3 | var hello = 'Hello' 4 | 5 | const rootDom = document.querySelector('#root')! 6 | rootDom.innerHTML = hello + text 7 | 8 | export {} 9 | -------------------------------------------------------------------------------- /playground/eslint-config-log-level/src/text.ts: -------------------------------------------------------------------------------- 1 | export const text = 'Vanilla JS/TS' 2 | -------------------------------------------------------------------------------- /playground/eslint-config-log-level/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 5 | "types": ["vite/client"], 6 | "allowJs": false, 7 | "skipLibCheck": false, 8 | "esModuleInterop": false, 9 | "allowSyntheticDefaultImports": true, 10 | "strict": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "module": "ESNext", 13 | "moduleResolution": "Node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": true 17 | }, 18 | "include": ["./src"] 19 | } 20 | -------------------------------------------------------------------------------- /playground/eslint-config-log-level/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import checker from 'vite-plugin-checker' 3 | 4 | export default defineConfig({ 5 | plugins: [ 6 | checker({ 7 | eslint: { 8 | lintCommand: 'eslint ./src --ext .ts', 9 | dev: { logLevel: ['warning'] }, 10 | }, 11 | }), 12 | ], 13 | }) 14 | -------------------------------------------------------------------------------- /playground/eslint-default/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended"] 4 | } 5 | -------------------------------------------------------------------------------- /playground/eslint-default/__tests__/test.spec.ts: -------------------------------------------------------------------------------- 1 | import stringify from 'fast-json-stable-stringify' 2 | import { describe, expect, it } from 'vitest' 3 | import { 4 | diagnostics, 5 | editFile, 6 | expectStderrContains, 7 | isBuild, 8 | isServe, 9 | log, 10 | resetReceivedLog, 11 | sleepForEdit, 12 | sleepForServerReady, 13 | stripedLog, 14 | } from '../../testUtils' 15 | 16 | describe('eslint-default', () => { 17 | describe.runIf(isServe)('serve', () => { 18 | it('get initial error and subsequent error', async () => { 19 | await sleepForServerReady() 20 | expect(stringify(diagnostics)).toMatchSnapshot() 21 | expect(stripedLog).toMatchSnapshot() 22 | 23 | console.log('-- edit error file --') 24 | resetReceivedLog() 25 | editFile('src/main.ts', (code) => code.replace(`'Hello'`, `'Hello~'`)) 26 | await sleepForEdit() 27 | expect(stringify(diagnostics)).toMatchSnapshot() 28 | expect(stripedLog).toMatchSnapshot() 29 | 30 | console.log('-- edit non error file --') 31 | resetReceivedLog() 32 | editFile('src/text.ts', (code) => code.replace(`Vanilla`, `vanilla`)) 33 | await sleepForEdit() 34 | expect(stringify(diagnostics)).toMatchSnapshot() 35 | expect(stripedLog).toMatchSnapshot() 36 | }) 37 | }) 38 | 39 | describe.runIf(isBuild)('build', () => { 40 | const expectedMsg = 'Unexpected var, use let or const instead' 41 | 42 | it('should fail', async () => { 43 | expectStderrContains(log, expectedMsg) 44 | }) 45 | }) 46 | }) 47 | -------------------------------------------------------------------------------- /playground/eslint-default/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite App 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /playground/eslint-default/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@playground/eslint-default", 3 | "version": "0.0.0", 4 | "private": true, 5 | "type": "module", 6 | "scripts": { 7 | "build": "vite build", 8 | "dev": "vite", 9 | "lint": "eslint --ext .js,.ts ./src/**", 10 | "serve": "vite preview" 11 | }, 12 | "devDependencies": { 13 | "@typescript-eslint/eslint-plugin": "^5.62.0", 14 | "@typescript-eslint/parser": "^5.62.0", 15 | "eslint": "^8.57.1", 16 | "typescript": "^5.8.3", 17 | "vite": "^6.3.4", 18 | "vite-plugin-checker": "workspace:*" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /playground/eslint-default/src/main.ts: -------------------------------------------------------------------------------- 1 | import { text } from './text' 2 | 3 | let count: string = 0 4 | var hello = 'Hello' 5 | 6 | const rootDom = document.querySelector('#root')! 7 | rootDom.innerHTML = hello + text 8 | 9 | export {} 10 | -------------------------------------------------------------------------------- /playground/eslint-default/src/text.ts: -------------------------------------------------------------------------------- 1 | export const text = 'Vanilla JS/TS' 2 | -------------------------------------------------------------------------------- /playground/eslint-default/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 5 | "types": ["vite/client"], 6 | "allowJs": false, 7 | "skipLibCheck": false, 8 | "esModuleInterop": false, 9 | "allowSyntheticDefaultImports": true, 10 | "strict": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "module": "ESNext", 13 | "moduleResolution": "Node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": true 17 | }, 18 | "include": ["./src"] 19 | } 20 | -------------------------------------------------------------------------------- /playground/eslint-default/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import checker from 'vite-plugin-checker' 3 | 4 | export default defineConfig({ 5 | plugins: [ 6 | checker({ 7 | eslint: { 8 | lintCommand: 'eslint ./src --ext .ts', 9 | }, 10 | }), 11 | ], 12 | }) 13 | -------------------------------------------------------------------------------- /playground/eslint-flat/__tests__/__snapshots__/test.spec.ts.snap: -------------------------------------------------------------------------------- 1 | // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html 2 | 3 | exports[`eslint > serve > get initial error and subsequent error 1`] = `"[{"checkerId":"ESLint","frame":" > 1 | const a = /"hello/"/n | ^/n > 2 |/n | ^","id":"/playground-temp/eslint-flat/src/index.js","level":1,"loc":{"column":18,"file":"/playground-temp/eslint-flat/src/index.js","line":1},"message":"Missing semicolon. (semi)","stack":""}]"`; 4 | 5 | exports[`eslint > serve > get initial error and subsequent error 2`] = ` 6 | [ 7 | " ERROR(ESLint) Missing semicolon. (semi) 8 | FILE /playground-temp/eslint-flat/src/index.js:1:18 9 | 10 | > 1 | const a = "hello" 11 | | ^ 12 | > 2 | 13 | | ^ 14 | ", 15 | "[ESLint] Found 1 error and 0 warning", 16 | ] 17 | `; 18 | 19 | exports[`eslint > serve > get initial error and subsequent error 3`] = `"[{"checkerId":"ESLint","frame":" > 1 | const a = /"hello/"/n | ^/n > 2 |/n | ^","id":"/playground-temp/eslint-flat/src/index.js","level":1,"loc":{"column":18,"file":"/playground-temp/eslint-flat/src/index.js","line":1},"message":"Missing semicolon. (semi)","stack":""},{"checkerId":"ESLint","frame":" > 1 | const a = /"hello/"/n | ^/n > 2 |/n | ^","id":"/playground-temp/eslint-flat/src/index.js","level":1,"loc":{"column":18,"file":"/playground-temp/eslint-flat/src/index.js","line":1},"message":"Missing semicolon. (semi)","stack":""}]"`; 20 | 21 | exports[`eslint > serve > get initial error and subsequent error 4`] = ` 22 | [ 23 | " ERROR(ESLint) Missing semicolon. (semi) 24 | FILE /playground-temp/eslint-flat/src/index.js:1:18 25 | 26 | > 1 | const a = "hello" 27 | | ^ 28 | > 2 | 29 | | ^ 30 | ", 31 | "[ESLint] Found 1 error and 0 warning", 32 | ] 33 | `; 34 | -------------------------------------------------------------------------------- /playground/eslint-flat/__tests__/test.spec.ts: -------------------------------------------------------------------------------- 1 | import stringify from 'fast-json-stable-stringify' 2 | import { describe, expect, it } from 'vitest' 3 | import { 4 | diagnostics, 5 | editFile, 6 | expectStderrContains, 7 | isBuild, 8 | isServe, 9 | log, 10 | resetReceivedLog, 11 | sleepForEdit, 12 | sleepForServerReady, 13 | stripedLog, 14 | } from '../../testUtils' 15 | 16 | describe('eslint', () => { 17 | describe.runIf(isServe)('serve', () => { 18 | it('get initial error and subsequent error', async () => { 19 | await sleepForServerReady() 20 | expect(stringify(diagnostics)).toMatchSnapshot() 21 | expect(stripedLog).toMatchSnapshot() 22 | 23 | console.log('-- edit error file --') 24 | resetReceivedLog() 25 | editFile('src/index.js', (code) => code.replace(`Hello`, `Hello~`)) 26 | await sleepForEdit() 27 | expect(stringify(diagnostics)).toMatchSnapshot() 28 | expect(stripedLog).toMatchSnapshot() 29 | }) 30 | }) 31 | 32 | describe.runIf(isBuild)('build', () => { 33 | const expectedMsg = 'Missing semicolon' 34 | 35 | it('should fail', async () => { 36 | expectStderrContains(log, expectedMsg) 37 | }) 38 | }) 39 | }) 40 | -------------------------------------------------------------------------------- /playground/eslint-flat/eslint.config.js: -------------------------------------------------------------------------------- 1 | export default [ 2 | { 3 | files: ["src/**/*"], 4 | rules: { 5 | semi: 'error', 6 | }, 7 | }, 8 | ] 9 | -------------------------------------------------------------------------------- /playground/eslint-flat/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite App 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /playground/eslint-flat/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@playground/eslint-flat", 3 | "version": "0.0.0", 4 | "private": true, 5 | "type": "module", 6 | "scripts": { 7 | "build": "vite build", 8 | "dev": "vite", 9 | "lint": "eslint .", 10 | "serve": "vite preview" 11 | }, 12 | "devDependencies": { 13 | "eslint": "^9.26.0", 14 | "vite": "^6.3.4", 15 | "vite-plugin-checker": "workspace:*" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /playground/eslint-flat/src/index.js: -------------------------------------------------------------------------------- 1 | const a = "hello" 2 | -------------------------------------------------------------------------------- /playground/eslint-flat/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 5 | "types": ["vite/client"], 6 | "allowJs": false, 7 | "skipLibCheck": false, 8 | "esModuleInterop": false, 9 | "allowSyntheticDefaultImports": true, 10 | "strict": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "module": "ESNext", 13 | "moduleResolution": "Node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": true 17 | }, 18 | "include": ["./src"] 19 | } 20 | -------------------------------------------------------------------------------- /playground/eslint-flat/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import checker from 'vite-plugin-checker' 3 | 4 | export default defineConfig({ 5 | plugins: [ 6 | checker({ 7 | eslint: { 8 | lintCommand: 'eslint "./src/**/*.js"', 9 | useFlatConfig: true, 10 | }, 11 | }), 12 | ], 13 | }) 14 | -------------------------------------------------------------------------------- /playground/multiple-hmr/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended"] 4 | } 5 | -------------------------------------------------------------------------------- /playground/multiple-hmr/__tests__/test.spec.ts: -------------------------------------------------------------------------------- 1 | import stringify from 'fast-json-stable-stringify' 2 | import stable from 'sort-deep-object-arrays' 3 | import { describe, expect, it } from 'vitest' 4 | 5 | import { 6 | diagnostics, 7 | editFile, 8 | expectStderrContains, 9 | isBuild, 10 | isServe, 11 | resetDiagnostics, 12 | resetReceivedLog, 13 | log, 14 | sleepForEdit, 15 | sleepForServerReady, 16 | stripedLog, 17 | } from '../../testUtils' 18 | 19 | describe('multiple-hmr', () => { 20 | describe.runIf(isServe)('serve', () => { 21 | it('get initial error and subsequent error', async () => { 22 | await sleepForServerReady() 23 | expect(stringify(stable(diagnostics))).toMatchSnapshot() 24 | expect(stable(stripedLog)).toMatchSnapshot() 25 | 26 | console.log('-- edit with error --') 27 | resetDiagnostics() 28 | resetReceivedLog() 29 | editFile('src/value.ts', (code) => 30 | code.replace('export var value: string = 1', 'export var value: string = 2') 31 | ) 32 | await sleepForEdit() 33 | expect(stringify(stable(diagnostics))).toMatchSnapshot() 34 | // don't know why striped log in disorder on Linux, while correct on mac and Windows 35 | // comment out for now to pass test cases stably and striped log is duplicated with diagnostics somehow. 36 | // Need help to figure out what went wrong. 😅 37 | // expect(stripedLog).toMatchSnapshot() 38 | 39 | console.log('-- fix typescript error --') 40 | resetDiagnostics() 41 | resetReceivedLog() 42 | editFile('src/value.ts', (code) => 43 | code.replace('export var value: string = 2', `export var value = 2`) 44 | ) 45 | await sleepForEdit() 46 | expect(stringify(stable(diagnostics))).toMatchSnapshot() 47 | 48 | console.log('-- fix eslint error --') 49 | resetDiagnostics() 50 | resetReceivedLog() 51 | editFile('src/value.ts', (code) => code.replace('var', 'const')) 52 | await sleepForEdit() 53 | expect(stringify(stable(diagnostics))).toMatchSnapshot() 54 | }) 55 | }) 56 | 57 | describe.runIf(isBuild)('build', () => { 58 | const expectedMsg = [ 59 | 'Unexpected var, use let or const instead', 60 | `error TS2322: Type 'number' is not assignable to type 'string'`, 61 | ] 62 | 63 | it('should fail', async () => { 64 | expectStderrContains(log, expectedMsg) 65 | }) 66 | }) 67 | }) 68 | -------------------------------------------------------------------------------- /playground/multiple-hmr/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite App 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /playground/multiple-hmr/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@playground/multiple-hmr", 3 | "version": "0.0.0", 4 | "private": true, 5 | "type": "module", 6 | "scripts": { 7 | "build": "vite build", 8 | "dev": "vite", 9 | "serve": "vite preview" 10 | }, 11 | "dependencies": { 12 | "react": "^19.1.0", 13 | "react-dom": "^19.1.0" 14 | }, 15 | "devDependencies": { 16 | "@types/react": "^19.1.3", 17 | "@types/react-dom": "^19.1.3", 18 | "@vitejs/plugin-react": "^4.4.1", 19 | "typescript": "^5.8.3", 20 | "vite": "^6.3.4", 21 | "vite-plugin-checker": "workspace:*" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /playground/multiple-hmr/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | height: 40vmin; 7 | pointer-events: none; 8 | } 9 | 10 | @media (prefers-reduced-motion: no-preference) { 11 | .App-logo { 12 | animation: App-logo-spin infinite 20s linear; 13 | } 14 | } 15 | 16 | .App-header { 17 | background-color: #282c34; 18 | min-height: 100vh; 19 | display: flex; 20 | flex-direction: column; 21 | align-items: center; 22 | justify-content: center; 23 | font-size: calc(10px + 2vmin); 24 | color: white; 25 | } 26 | 27 | .App-link { 28 | color: #61dafb; 29 | } 30 | 31 | @keyframes App-logo-spin { 32 | from { 33 | transform: rotate(0deg); 34 | } 35 | to { 36 | transform: rotate(360deg); 37 | } 38 | } 39 | 40 | button { 41 | font-size: calc(10px + 2vmin); 42 | } 43 | -------------------------------------------------------------------------------- /playground/multiple-hmr/src/App.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react' 2 | import logo from './logo.svg' 3 | import { value } from './value' 4 | import './App.css' 5 | 6 | function App() { 7 | const [count, setCount] = useState(0) 8 | return ( 9 |
10 |
11 | logo 12 |

Hello Vite + React! ({value})

13 |

14 | 15 |

16 |

17 | Edit App.tsx and save to test HMR updates. 18 |

19 |

20 | 26 | Learn React 27 | 28 | {' | '} 29 | 35 | Vite Docs 36 | 37 |

38 |
39 |
40 | ) 41 | } 42 | 43 | export default App 44 | -------------------------------------------------------------------------------- /playground/multiple-hmr/src/favicon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /playground/multiple-hmr/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", 4 | "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", 5 | sans-serif; 6 | -webkit-font-smoothing: antialiased; 7 | -moz-osx-font-smoothing: grayscale; 8 | } 9 | 10 | code { 11 | font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", 12 | monospace; 13 | } 14 | -------------------------------------------------------------------------------- /playground/multiple-hmr/src/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /playground/multiple-hmr/src/main.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { createRoot } from 'react-dom/client' 3 | 4 | import App from './App' 5 | import './index.css' 6 | 7 | const container = document.getElementById('root') as HTMLElement 8 | const root = createRoot(container) 9 | root.render( 10 | 11 | 12 | 13 | ) 14 | -------------------------------------------------------------------------------- /playground/multiple-hmr/src/value.ts: -------------------------------------------------------------------------------- 1 | export var value: string = 1 2 | -------------------------------------------------------------------------------- /playground/multiple-hmr/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 5 | "types": ["vite/client"], 6 | "allowJs": false, 7 | "skipLibCheck": false, 8 | "esModuleInterop": false, 9 | "allowSyntheticDefaultImports": true, 10 | "strict": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "module": "ESNext", 13 | "moduleResolution": "Node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": true, 17 | "jsx": "react" 18 | }, 19 | "include": ["./src"] 20 | } 21 | -------------------------------------------------------------------------------- /playground/multiple-hmr/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react' 3 | import checker from 'vite-plugin-checker' 4 | 5 | // https://vitejs.dev/config/ 6 | export default defineConfig({ 7 | plugins: [ 8 | 9 | react(), 10 | checker({ 11 | typescript: true, 12 | eslint: { 13 | lintCommand: 'eslint ./src --ext .ts', 14 | }, 15 | }), 16 | ], 17 | }) 18 | -------------------------------------------------------------------------------- /playground/multiple-reload/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended"] 4 | } 5 | -------------------------------------------------------------------------------- /playground/multiple-reload/__tests__/test.spec.ts: -------------------------------------------------------------------------------- 1 | import stringify from 'fast-json-stable-stringify' 2 | import stable from 'sort-deep-object-arrays' 3 | import { describe, expect, it } from 'vitest' 4 | 5 | import { 6 | diagnostics, 7 | editFile, 8 | expectStderrContains, 9 | isBuild, 10 | isServe, 11 | resetDiagnostics, 12 | resetReceivedLog, 13 | log, 14 | sleepForEdit, 15 | sleepForServerReady, 16 | stripedLog, 17 | } from '../../testUtils' 18 | 19 | describe('multiple-reload', () => { 20 | describe.runIf(isServe)('serve', () => { 21 | it('get initial error and subsequent error', async () => { 22 | await sleepForServerReady() 23 | expect(stringify(stable(diagnostics))).toMatchSnapshot() 24 | expect(stable(stripedLog)).toMatchSnapshot() 25 | 26 | console.log('-- edit with error --') 27 | resetDiagnostics() 28 | resetReceivedLog() 29 | editFile('src/main.ts', (code) => 30 | code.replace(`var count: number = 0`, `var count: number = 1`) 31 | ) 32 | await sleepForEdit() 33 | expect(stringify(stable(diagnostics))).toMatchSnapshot() 34 | // don't know why striped log in disorder on Linux, while correct on mac and Windows 35 | // comment out for now to pass test cases stably and striped log is duplicated with diagnostics somehow. 36 | // Need help to figure out what went wrong. 😅 37 | // expect(stable(stripedLog)).toMatchSnapshot() 38 | 39 | console.log('-- fix typescript error --') 40 | resetDiagnostics() 41 | resetReceivedLog() 42 | editFile('src/main.ts', (code) => 43 | code.replace('var count: number = 1', `var count: string = 1`) 44 | ) 45 | await sleepForEdit() 46 | expect(stringify(stable(diagnostics))).toMatchSnapshot() 47 | 48 | console.log('-- fix eslint error --') 49 | resetDiagnostics() 50 | resetReceivedLog() 51 | editFile('src/main.ts', (code) => code.replace('var count: string = 1', 'const count = 0')) 52 | await sleepForEdit() 53 | expect(stringify(stable(diagnostics))).toMatchSnapshot() 54 | }) 55 | }) 56 | 57 | describe.runIf(isBuild)('build', () => { 58 | const expectedMsg = [ 59 | 'Unexpected var, use let or const instead', 60 | `error TS2322: Type 'number' is not assignable to type 'string'`, 61 | ] 62 | 63 | it('should fail', async () => { 64 | expectStderrContains(log, expectedMsg) 65 | }) 66 | }) 67 | }) 68 | -------------------------------------------------------------------------------- /playground/multiple-reload/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite App 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /playground/multiple-reload/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@playground/multiple-reload", 3 | "version": "0.0.0", 4 | "private": true, 5 | "type": "module", 6 | "scripts": { 7 | "build": "vite build", 8 | "dev": "vite", 9 | "lint": "eslint --ext .js,.ts ./src/**", 10 | "serve": "vite preview" 11 | }, 12 | "devDependencies": { 13 | "@typescript-eslint/eslint-plugin": "^5.62.0", 14 | "@typescript-eslint/parser": "^5.62.0", 15 | "eslint": "^8.57.1", 16 | "typescript": "^5.8.3", 17 | "vite": "^6.3.4", 18 | "vite-plugin-checker": "workspace:*" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /playground/multiple-reload/src/main.ts: -------------------------------------------------------------------------------- 1 | import { text } from './text' 2 | 3 | var count: string = 0 4 | const hello = 'Hello' 5 | 6 | const rootDom = document.querySelector('#root')! 7 | rootDom.innerHTML = hello + text + count 8 | 9 | export {} 10 | -------------------------------------------------------------------------------- /playground/multiple-reload/src/text.ts: -------------------------------------------------------------------------------- 1 | export const text = 'Multiple Checkers' 2 | -------------------------------------------------------------------------------- /playground/multiple-reload/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 5 | "types": ["vite/client"], 6 | "allowJs": false, 7 | "skipLibCheck": false, 8 | "esModuleInterop": false, 9 | "allowSyntheticDefaultImports": true, 10 | "strict": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "module": "ESNext", 13 | "moduleResolution": "Node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": true 17 | }, 18 | "include": ["./src"] 19 | } 20 | -------------------------------------------------------------------------------- /playground/multiple-reload/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import checker from 'vite-plugin-checker' 3 | 4 | export default defineConfig({ 5 | plugins: [ 6 | checker({ 7 | typescript: true, 8 | eslint: { 9 | lintCommand: 'eslint ./src --ext .ts', 10 | }, 11 | }), 12 | ], 13 | }) 14 | -------------------------------------------------------------------------------- /playground/serializers.ts: -------------------------------------------------------------------------------- 1 | import type { SnapshotSerializer } from 'vitest' 2 | 3 | function normalizePaths(val: string) { 4 | return val 5 | .replace(/\\/gim, '/') // replace slashes 6 | .replace(/\/\//gim, '/') // replace slashes 7 | .replace(/[a-zA-Z]:\//gim, '/') // Remove win32 drive letters, C:\ -> \ 8 | } 9 | 10 | function createResult(val: string) { 11 | const cwd = normalizePaths(process.cwd()) 12 | 13 | return normalizePaths(val) 14 | .replaceAll(cwd, '') 15 | .replace(/\/r\/n/gim, '/n') 16 | } 17 | 18 | export const normalizeLogSerializer: SnapshotSerializer = { 19 | print(val: string, print) { 20 | return print(createResult(val)) 21 | }, 22 | test(val) { 23 | return typeof val === 'string' && val && createResult(val) !== val 24 | }, 25 | } 26 | -------------------------------------------------------------------------------- /playground/stylelint-default/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended"] 4 | } 5 | -------------------------------------------------------------------------------- /playground/stylelint-default/.stylelintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "stylelint-config-standard" 3 | } 4 | -------------------------------------------------------------------------------- /playground/stylelint-default/__tests__/test.spec.ts: -------------------------------------------------------------------------------- 1 | import stringify from 'fast-json-stable-stringify' 2 | import { describe, expect, it } from 'vitest' 3 | import { 4 | diagnostics, 5 | editFile, 6 | expectStderrContains, 7 | isBuild, 8 | isServe, 9 | log, 10 | resetReceivedLog, 11 | sleepForEdit, 12 | sleepForServerReady, 13 | stripedLog, 14 | } from '../../testUtils' 15 | 16 | describe('stylelint', () => { 17 | describe.runIf(isServe)('serve', () => { 18 | it('get initial error and subsequent error', async () => { 19 | await sleepForServerReady() 20 | expect(stringify(diagnostics)).toMatchSnapshot() 21 | expect(stripedLog).toMatchSnapshot() 22 | 23 | console.log('-- edit error file --') 24 | resetReceivedLog() 25 | editFile('src/style.css', (code) => code.replace(`color: rgb(0, 0, 0);`, `color: #fff;`)) 26 | await sleepForEdit() 27 | expect(stringify(diagnostics)).toMatchSnapshot() 28 | expect(stripedLog).toMatchSnapshot() 29 | }) 30 | }) 31 | 32 | describe.runIf(isBuild)('build', () => { 33 | const expectedMsg = ['Unexpected empty block', 'Expected modern color-function notation'] 34 | 35 | it('should fail', async () => { 36 | expectStderrContains(log, expectedMsg) 37 | }) 38 | }) 39 | }) 40 | -------------------------------------------------------------------------------- /playground/stylelint-default/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite App 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /playground/stylelint-default/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@playground/stylelint-default", 3 | "version": "0.0.0", 4 | "private": true, 5 | "type": "module", 6 | "scripts": { 7 | "build": "vite build", 8 | "dev": "vite", 9 | "lint": "stylelint \"./**/*.css\"", 10 | "serve": "vite preview" 11 | }, 12 | "devDependencies": { 13 | "meow": "^13.2.0", 14 | "stylelint": "^16.19.1", 15 | "stylelint-config-standard": "^38.0.0", 16 | "typescript": "^5.8.3", 17 | "vite": "^6.3.4", 18 | "vite-plugin-checker": "workspace:*" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /playground/stylelint-default/src/main.ts: -------------------------------------------------------------------------------- 1 | import './style.css' 2 | 3 | const rootDom = document.querySelector('#root')! 4 | rootDom.innerHTML = 'Hello world.' 5 | 6 | export {} 7 | -------------------------------------------------------------------------------- /playground/stylelint-default/src/style.css: -------------------------------------------------------------------------------- 1 | #root { 2 | } 3 | 4 | body { 5 | color: rgb(0, 0, 0); 6 | } 7 | -------------------------------------------------------------------------------- /playground/stylelint-default/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 5 | "types": ["vite/client"], 6 | "allowJs": false, 7 | "skipLibCheck": false, 8 | "esModuleInterop": false, 9 | "allowSyntheticDefaultImports": true, 10 | "strict": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "module": "ESNext", 13 | "moduleResolution": "Node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": true 17 | }, 18 | "include": ["./src"] 19 | } 20 | -------------------------------------------------------------------------------- /playground/stylelint-default/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import checker from 'vite-plugin-checker' 3 | 4 | export default defineConfig({ 5 | plugins: [ 6 | checker({ 7 | stylelint: { 8 | lintCommand: 'stylelint "./**/*.css"', 9 | }, 10 | }), 11 | ], 12 | }) 13 | -------------------------------------------------------------------------------- /playground/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "include": ["./"], 3 | "exclude": ["**/dist/**"], 4 | "compilerOptions": { 5 | "lib": ["ESNext"], 6 | "target": "ES2020", 7 | "module": "ESNext", 8 | "outDir": "dist", 9 | "baseUrl": ".", 10 | "allowJs": true, 11 | "esModuleInterop": true, 12 | "resolveJsonModule": true, 13 | "moduleResolution": "Node", 14 | "skipLibCheck": true, 15 | "noUnusedLocals": true, 16 | "jsx": "preserve" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /playground/typescript-react/__tests__/test.spec.ts: -------------------------------------------------------------------------------- 1 | import stringify from 'fast-json-stable-stringify' 2 | import { describe, expect, it } from 'vitest' 3 | 4 | import { 5 | diagnostics, 6 | editFile, 7 | expectStderrContains, 8 | isBuild, 9 | isServe, 10 | log, 11 | resetReceivedLog, 12 | sleepForEdit, 13 | sleepForServerReady, 14 | stripedLog, 15 | } from '../../testUtils' 16 | 17 | describe('typescript-react', () => { 18 | describe.runIf(isServe)('serve', () => { 19 | it('get initial error and subsequent error', async () => { 20 | await sleepForServerReady() 21 | expect(stringify(diagnostics)).toMatchSnapshot() 22 | expect(stripedLog).toMatchSnapshot() 23 | 24 | console.log('-- edit file --') 25 | resetReceivedLog() 26 | editFile('src/App.tsx', (code) => code.replace('useState(1)', 'useState(2)')) 27 | await sleepForEdit() 28 | expect(stringify(diagnostics)).toMatchSnapshot() 29 | expect(stripedLog).toMatchSnapshot() 30 | }) 31 | }) 32 | 33 | describe.runIf(isBuild)('build', () => { 34 | it('should fail', async () => { 35 | expectStderrContains(log, 'error TS2345') 36 | }) 37 | }) 38 | }) 39 | -------------------------------------------------------------------------------- /playground/typescript-react/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite App 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /playground/typescript-react/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@playground/typescript-react", 3 | "version": "0.0.0", 4 | "private": true, 5 | "type": "module", 6 | "scripts": { 7 | "build": "vite build", 8 | "dev": "vite", 9 | "serve": "vite preview" 10 | }, 11 | "dependencies": { 12 | "react": "^19.1.0", 13 | "react-dom": "^19.1.0" 14 | }, 15 | "devDependencies": { 16 | "@types/react": "^19.1.3", 17 | "@types/react-dom": "^19.1.3", 18 | "@vitejs/plugin-react": "^4.4.1", 19 | "typescript": "^5.8.3", 20 | "vite": "^6.3.4", 21 | "vite-plugin-checker": "workspace:*" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /playground/typescript-react/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | height: 40vmin; 7 | pointer-events: none; 8 | } 9 | 10 | @media (prefers-reduced-motion: no-preference) { 11 | .App-logo { 12 | animation: App-logo-spin infinite 20s linear; 13 | } 14 | } 15 | 16 | .App-header { 17 | background-color: #282c34; 18 | min-height: 100vh; 19 | display: flex; 20 | flex-direction: column; 21 | align-items: center; 22 | justify-content: center; 23 | font-size: calc(10px + 2vmin); 24 | color: white; 25 | } 26 | 27 | .App-link { 28 | color: #61dafb; 29 | } 30 | 31 | @keyframes App-logo-spin { 32 | from { 33 | transform: rotate(0deg); 34 | } 35 | to { 36 | transform: rotate(360deg); 37 | } 38 | } 39 | 40 | button { 41 | font-size: calc(10px + 2vmin); 42 | } 43 | -------------------------------------------------------------------------------- /playground/typescript-react/src/App.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react' 2 | import logo from './logo.svg' 3 | import './App.css' 4 | 5 | function App() { 6 | const [count, setCount] = useState(1) 7 | return ( 8 |
9 |
10 | logo 11 |

Hello Vite + React!

12 |

13 | 14 |

15 |

16 | Edit App.tsx and save to test HMR updates. 17 |

18 |

19 | 25 | Learn React 26 | 27 | {' | '} 28 | 34 | Vite Docs 35 | 36 |

37 |
38 |
39 | ) 40 | } 41 | 42 | export default App 43 | -------------------------------------------------------------------------------- /playground/typescript-react/src/favicon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /playground/typescript-react/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", 4 | "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", 5 | sans-serif; 6 | -webkit-font-smoothing: antialiased; 7 | -moz-osx-font-smoothing: grayscale; 8 | } 9 | 10 | code { 11 | font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", 12 | monospace; 13 | } 14 | -------------------------------------------------------------------------------- /playground/typescript-react/src/main.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { createRoot } from 'react-dom/client' 3 | 4 | import App from './App' 5 | import './index.css' 6 | 7 | const container = document.getElementById('root') as HTMLElement 8 | const root = createRoot(container) 9 | root.render( 10 | 11 | 12 | 13 | ) 14 | -------------------------------------------------------------------------------- /playground/typescript-react/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 5 | "types": ["vite/client"], 6 | "allowJs": false, 7 | "skipLibCheck": false, 8 | "esModuleInterop": false, 9 | "allowSyntheticDefaultImports": true, 10 | "strict": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "module": "ESNext", 13 | "moduleResolution": "Node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": true, 17 | "jsx": "react" 18 | }, 19 | "include": ["./src"] 20 | } 21 | -------------------------------------------------------------------------------- /playground/typescript-react/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react' 3 | import checker from 'vite-plugin-checker' 4 | 5 | // https://vitejs.dev/config/ 6 | export default defineConfig({ 7 | plugins: [ 8 | react(), 9 | checker({ 10 | typescript: true, 11 | }), 12 | ], 13 | }) 14 | -------------------------------------------------------------------------------- /playground/vitestGlobalSetup.ts: -------------------------------------------------------------------------------- 1 | import { rmSync } from 'node:fs' 2 | import fs from 'node:fs/promises' 3 | import os from 'node:os' 4 | import path from 'node:path' 5 | import { chromium } from 'playwright-chromium' 6 | 7 | import type { BrowserServer } from 'playwright-chromium' 8 | const DIR = path.join(os.tmpdir(), 'vitest_playwright_global_setup') 9 | 10 | let browserServer: BrowserServer | undefined 11 | 12 | export async function setup(): Promise { 13 | browserServer = await chromium.launchServer({ 14 | headless: !process.env.VITE_DEBUG_SERVE, 15 | args: process.env.CI ? ['--no-sandbox', '--disable-setuid-sandbox'] : undefined, 16 | }) 17 | 18 | await fs.mkdir(DIR, { recursive: true }) 19 | await fs.writeFile(path.join(DIR, 'wsEndpoint'), browserServer.wsEndpoint()) 20 | 21 | const tempDir = path.resolve(__dirname, '../playground-temp') 22 | await fs.rm(tempDir, { force: true, recursive: true }) 23 | await fs.mkdir(tempDir, { recursive: true }) 24 | await fs 25 | .cp(path.resolve(__dirname, '../playground'), tempDir, { 26 | dereference: false, 27 | recursive: true, 28 | filter(file) { 29 | const _file = file.replace(/\\/g, '/') 30 | return !_file.includes('__tests__') && !file.match(/dist(\/|$)/) 31 | }, 32 | }) 33 | .catch(async (error) => { 34 | if (error.code === 'EPERM' && error.syscall === 'symlink') { 35 | throw new Error( 36 | 'Could not create symlinks. On Windows, consider activating Developer Mode to allow non-admin users to create symlinks by following the instructions at https://docs.microsoft.com/en-us/windows/apps/get-started/enable-your-device-for-development.' 37 | ) 38 | } else { 39 | throw error 40 | } 41 | }) 42 | } 43 | 44 | export async function teardown(): Promise { 45 | browserServer?.close() 46 | if (!process.env.VITE_PRESERVE_BUILD_ARTIFACTS) { 47 | rmSync(path.resolve(__dirname, '../playground-temp'), { force: true, recursive: true }) 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /playground/vls-vue2/.browserslistrc: -------------------------------------------------------------------------------- 1 | > 1% 2 | last 2 versions 3 | not dead 4 | -------------------------------------------------------------------------------- /playground/vls-vue2/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { 4 | node: true, 5 | }, 6 | extends: [ 7 | 'plugin:vue/essential', 8 | 'eslint:recommended', 9 | '@vue/typescript/recommended', 10 | '@vue/prettier', 11 | ], 12 | parserOptions: { 13 | ecmaVersion: 2020, 14 | parser: '@typescript-eslint/parser', 15 | }, 16 | rules: { 17 | 'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off', 18 | 'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off', 19 | }, 20 | } 21 | -------------------------------------------------------------------------------- /playground/vls-vue2/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | 5 | 6 | # local env files 7 | .env.local 8 | .env.*.local 9 | 10 | # Log files 11 | npm-debug.log* 12 | yarn-debug.log* 13 | yarn-error.log* 14 | pnpm-debug.log* 15 | 16 | # Editor directories and files 17 | .idea 18 | .vscode 19 | *.suo 20 | *.ntvs* 21 | *.njsproj 22 | *.sln 23 | *.sw? 24 | 25 | pnpm-lock.yaml -------------------------------------------------------------------------------- /playground/vls-vue2/.npmrc: -------------------------------------------------------------------------------- 1 | shamefully-hoist=true 2 | -------------------------------------------------------------------------------- /playground/vls-vue2/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Martinus Suherman 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 | -------------------------------------------------------------------------------- /playground/vls-vue2/README.md: -------------------------------------------------------------------------------- 1 | # Vue2 + Typescript + Vite Starter Template 2 | 3 | [![CodeFactor](https://www.codefactor.io/repository/github/martinussuherman/vue-template/badge)](https://www.codefactor.io/repository/github/martinussuherman/vue-template) [![CodeQL](https://github.com/martinussuherman/vue-template/actions/workflows/codeql-analysis.yml/badge.svg)](https://github.com/martinussuherman/vue-template/actions/workflows/codeql-analysis.yml) [![Publish to Github Pages](https://github.com/martinussuherman/vue-template/actions/workflows/gh-pages.yml/badge.svg)](https://github.com/martinussuherman/vue-template/actions/workflows/gh-pages.yml) 4 | --- 5 | 6 | Starter template example for Vue2 + Typescript + Vite. 7 | 8 | Note: Using pnpm for package manager. 9 | 10 | ## Project setup 11 | ``` 12 | pnpm install 13 | ``` 14 | 15 | ### Compiles and hot-reloads for development 16 | ``` 17 | pnpm run serve 18 | ``` 19 | 20 | ### Compiles and minifies for production 21 | ``` 22 | pnpm run build 23 | ``` 24 | 25 | ### Lints and fixes files 26 | ``` 27 | pnpm run lint 28 | ``` 29 | 30 | ### Customize configuration 31 | See [Configuration Reference](https://cli.vuejs.org/config/). 32 | -------------------------------------------------------------------------------- /playground/vls-vue2/__tests__/test.spec.ts: -------------------------------------------------------------------------------- 1 | import stringify from 'fast-json-stable-stringify' 2 | import { describe, expect, it } from 'vitest' 3 | 4 | import { 5 | diagnostics, 6 | editFile, 7 | expectStderrContains, 8 | isBuild, 9 | isServe, 10 | log, 11 | resetReceivedLog, 12 | sleepForEdit, 13 | sleepForServerReady, 14 | stripedLog, 15 | } from '../../testUtils' 16 | 17 | describe('vue2-vls', () => { 18 | describe.runIf(isServe)('serve', () => { 19 | it('get initial error and subsequent error', async () => { 20 | await sleepForServerReady() 21 | expect(stringify(diagnostics)).toMatchSnapshot() 22 | expect(stripedLog).toMatchSnapshot() 23 | 24 | console.log('-- edit file --') 25 | resetReceivedLog() 26 | editFile('src/components/HelloWorld.vue', (code) => code.replace('msg1', 'msg2')) 27 | await sleepForEdit() 28 | 29 | expect(stringify(diagnostics)).toMatchSnapshot() 30 | expect(stripedLog).toMatchSnapshot() 31 | }) 32 | }) 33 | 34 | // As per https://github.com/vuejs/vetur/issues/3686 35 | // We may remove VLS in the future to push the community to Vue 3 & Vue Language Tools. 36 | // describe.runIf(isBuild)('build', () => { 37 | // it('should fail', async () => { 38 | // expectStderrContains(log, `Property 'msg1' does not exist on type`) 39 | // }) 40 | // }) 41 | }) 42 | -------------------------------------------------------------------------------- /playground/vls-vue2/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Vue2 + Vite 9 | 10 | 11 | 14 |
15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /playground/vls-vue2/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@playground/vls-vue2", 3 | "version": "0.0.0", 4 | "private": true, 5 | "type": "module", 6 | "scripts": { 7 | "build": "vite build", 8 | "dev": "vite", 9 | "serve": "vite preview" 10 | }, 11 | "dependencies": { 12 | "vue": "^2.7.16", 13 | "vue-class-component": "7.2.6", 14 | "vue-property-decorator": "9.1.2", 15 | "vue-router": "3.6.5", 16 | "vuex": "3.6.2" 17 | }, 18 | "devDependencies": { 19 | "@types/node": "^18.19.87", 20 | "@typescript-eslint/eslint-plugin": "^4.33.0", 21 | "@typescript-eslint/parser": "^4.33.0", 22 | "@vue/eslint-config-prettier": "6.0.0", 23 | "@vue/eslint-config-typescript": "^7.0.0", 24 | "eslint": "^7.32.0", 25 | "eslint-plugin-prettier": "^3.4.1", 26 | "eslint-plugin-vue": "^7.20.0", 27 | "prettier": "3.5.3", 28 | "sass": "^1.87.0", 29 | "typescript": "~4.9.5", 30 | "vite": "^6.3.4", 31 | "vite-plugin-checker": "workspace:*", 32 | "vite-plugin-components": "^0.13.3", 33 | "vite-plugin-vue2": "^2.0.3", 34 | "vls": "^0.8.5", 35 | "vti": "^0.1.11", 36 | "vue-template-compiler": "^2.7.16" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /playground/vls-vue2/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fi3ework/vite-plugin-checker/78a8a7e1d748d333e77bddd3797734a13c8f0039/playground/vls-vue2/public/favicon.ico -------------------------------------------------------------------------------- /playground/vls-vue2/src/App.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 33 | -------------------------------------------------------------------------------- /playground/vls-vue2/src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fi3ework/vite-plugin-checker/78a8a7e1d748d333e77bddd3797734a13c8f0039/playground/vls-vue2/src/assets/logo.png -------------------------------------------------------------------------------- /playground/vls-vue2/src/main.ts: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import App from '@/App.vue' 3 | import router from '@/router' 4 | import store from '@/store' 5 | 6 | Vue.config.productionTip = false 7 | 8 | new Vue({ 9 | router, 10 | store, 11 | render: (h) => h(App), 12 | }).$mount('#app') 13 | 14 | export const str: string = 1 15 | -------------------------------------------------------------------------------- /playground/vls-vue2/src/router/index.ts: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import VueRouter, { RouteConfig } from 'vue-router' 3 | import Home from '@/views/Home.vue' 4 | 5 | Vue.use(VueRouter) 6 | 7 | const routes: Array = [ 8 | { 9 | path: '/', 10 | name: 'Home', 11 | component: Home, 12 | }, 13 | { 14 | path: '/about', 15 | name: 'About', 16 | // route level code-splitting 17 | // this generates a separate chunk (about.[hash].js) for this route 18 | // which is lazy-loaded when the route is visited. 19 | component: () => import(/* webpackChunkName: "about" */ '../views/About.vue'), 20 | }, 21 | ] 22 | 23 | const router = new VueRouter({ 24 | mode: 'history', 25 | base: import.meta.env.BASE_URL, 26 | routes, 27 | }) 28 | 29 | export default router 30 | -------------------------------------------------------------------------------- /playground/vls-vue2/src/shims-tsx.d.ts: -------------------------------------------------------------------------------- 1 | import Vue, { VNode } from 'vue' 2 | 3 | declare global { 4 | namespace JSX { 5 | // tslint:disable no-empty-interface 6 | interface Element extends VNode {} 7 | // tslint:disable no-empty-interface 8 | interface ElementClass extends Vue {} 9 | interface IntrinsicElements { 10 | [elem: string]: any 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /playground/vls-vue2/src/shims-vue.d.ts: -------------------------------------------------------------------------------- 1 | declare module '*.vue' { 2 | import Vue from 'vue' 3 | export default Vue 4 | } 5 | -------------------------------------------------------------------------------- /playground/vls-vue2/src/store/index.ts: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Vuex from 'vuex' 3 | 4 | Vue.use(Vuex) 5 | 6 | export default new Vuex.Store({ 7 | state: {}, 8 | mutations: {}, 9 | actions: {}, 10 | modules: {}, 11 | }) 12 | -------------------------------------------------------------------------------- /playground/vls-vue2/src/views/About.vue: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /playground/vls-vue2/src/views/Home.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 11 | -------------------------------------------------------------------------------- /playground/vls-vue2/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "module": "esnext", 5 | "strict": true, 6 | "jsx": "preserve", 7 | "importHelpers": true, 8 | "moduleResolution": "node", 9 | "experimentalDecorators": true, 10 | "skipLibCheck": true, 11 | "esModuleInterop": true, 12 | "allowSyntheticDefaultImports": true, 13 | "sourceMap": true, 14 | "baseUrl": ".", 15 | "types": ["vite/client"], 16 | "paths": { 17 | "@/*": ["src/*"] 18 | }, 19 | "lib": ["esnext", "dom", "dom.iterable", "scripthost"] 20 | }, 21 | "include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue", "tests/**/*.ts", "tests/**/*.tsx"], 22 | "exclude": ["node_modules"] 23 | } 24 | -------------------------------------------------------------------------------- /playground/vls-vue2/vetur.config.cjs: -------------------------------------------------------------------------------- 1 | // vetur.config.js 2 | /** @type {import('vls').VeturConfig} */ 3 | 4 | module.exports = { 5 | // **optional** default: `{}` 6 | // override vscode settings 7 | // Notice: It only affects the settings used by Vetur. 8 | settings: { 9 | 'vetur.useWorkspaceDependencies': true, 10 | 'vetur.experimental.templateInterpolationService': true, 11 | }, 12 | // **optional** default: `[{ root: './' }]` 13 | // support monorepos 14 | projects: ['./'], 15 | } 16 | -------------------------------------------------------------------------------- /playground/vls-vue2/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import { createVuePlugin } from 'vite-plugin-vue2' 3 | import ViteComponents from 'vite-plugin-components' 4 | import checker from 'vite-plugin-checker' 5 | import { resolve } from 'path' 6 | 7 | const config = defineConfig({ 8 | resolve: { 9 | alias: { 10 | '@': `${resolve(__dirname, 'src')}`, 11 | }, 12 | }, 13 | base: '/vue-template/', 14 | build: { 15 | minify: true, 16 | }, 17 | plugins: [ 18 | createVuePlugin({}), 19 | (ViteComponents.default || ViteComponents)({ transformer: 'vue2' }), 20 | checker({ vls: true }), 21 | ], 22 | }) 23 | 24 | export default config 25 | -------------------------------------------------------------------------------- /playground/vue-tsc-vue3/__tests__/test.spec.ts: -------------------------------------------------------------------------------- 1 | import stringify from 'fast-json-stable-stringify' 2 | import { describe, expect, it } from 'vitest' 3 | 4 | import { 5 | diagnostics, 6 | editFile, 7 | expectStderrContains, 8 | isBuild, 9 | isServe, 10 | log, 11 | resetReceivedLog, 12 | sleepForEdit, 13 | sleepForServerReady, 14 | stripedLog, 15 | } from '../../testUtils' 16 | 17 | describe('vue-tsc-vue3', () => { 18 | describe.runIf(isServe)('serve', () => { 19 | it('get initial error and subsequent error', async () => { 20 | await sleepForServerReady(2) 21 | expect(stringify(diagnostics)).toMatchSnapshot() 22 | expect(stripedLog).toMatchSnapshot() 23 | 24 | console.log('-- edit file --') 25 | resetReceivedLog() 26 | editFile('src/App.vue', (code) => 27 | code.replace('', '') 28 | ) 29 | await sleepForEdit(2) 30 | expect(stringify(diagnostics)).toMatchSnapshot() 31 | expect(stripedLog).toMatchSnapshot() 32 | }) 33 | }) 34 | 35 | describe.runIf(isBuild)('build', () => { 36 | it('should fail', async () => { 37 | const expectedMsg = `error TS2345` 38 | expectStderrContains(log, expectedMsg) 39 | }) 40 | }) 41 | }) 42 | -------------------------------------------------------------------------------- /playground/vue-tsc-vue3/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite App 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /playground/vue-tsc-vue3/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@playground/vue-tsc-vue3", 3 | "version": "0.0.0", 4 | "private": true, 5 | "type": "module", 6 | "scripts": { 7 | "build": "vite build", 8 | "dev": "vite", 9 | "serve": "vite preview" 10 | }, 11 | "dependencies": { 12 | "vue": "^3.5.13" 13 | }, 14 | "devDependencies": { 15 | "@vitejs/plugin-vue": "^5.2.4", 16 | "typescript": "^5.8.3", 17 | "vite": "^6.3.4", 18 | "vite-plugin-checker": "workspace:*", 19 | "vue-tsc": "~2.2.10" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /playground/vue-tsc-vue3/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fi3ework/vite-plugin-checker/78a8a7e1d748d333e77bddd3797734a13c8f0039/playground/vue-tsc-vue3/public/favicon.ico -------------------------------------------------------------------------------- /playground/vue-tsc-vue3/src/App.vue: -------------------------------------------------------------------------------- 1 | 5 | 6 | 17 | 18 | 28 | -------------------------------------------------------------------------------- /playground/vue-tsc-vue3/src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fi3ework/vite-plugin-checker/78a8a7e1d748d333e77bddd3797734a13c8f0039/playground/vue-tsc-vue3/src/assets/logo.png -------------------------------------------------------------------------------- /playground/vue-tsc-vue3/src/components/HelloWorld.vue: -------------------------------------------------------------------------------- 1 | 31 | 32 | 48 | 49 | 66 | -------------------------------------------------------------------------------- /playground/vue-tsc-vue3/src/main.ts: -------------------------------------------------------------------------------- 1 | import { createApp } from 'vue' 2 | import App from './App.vue' 3 | 4 | createApp(App).mount('#app') 5 | -------------------------------------------------------------------------------- /playground/vue-tsc-vue3/src/shims-vue.d.ts: -------------------------------------------------------------------------------- 1 | declare module '*.vue' { 2 | import { DefineComponent } from 'vue' 3 | const component: DefineComponent<{}, {}, any> 4 | export default component 5 | } 6 | -------------------------------------------------------------------------------- /playground/vue-tsc-vue3/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "module": "esnext", 5 | "moduleResolution": "node", 6 | "strict": true, 7 | "jsx": "preserve", 8 | "sourceMap": true, 9 | "resolveJsonModule": true, 10 | "esModuleInterop": true, 11 | "skipLibCheck": true, 12 | "lib": ["esnext", "dom"], 13 | "types": ["vite/client"] 14 | }, 15 | "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"] 16 | } 17 | -------------------------------------------------------------------------------- /playground/vue-tsc-vue3/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import vue from '@vitejs/plugin-vue' 3 | import checker from 'vite-plugin-checker' 4 | 5 | // https://vitejs.dev/config/ 6 | export default defineConfig({ 7 | plugins: [vue(), checker({ vueTsc: true })], 8 | }) 9 | -------------------------------------------------------------------------------- /pnpm-workspace.yaml: -------------------------------------------------------------------------------- 1 | packages: 2 | - 'playground/**' 3 | - 'packages/*' 4 | - 'docs' 5 | -------------------------------------------------------------------------------- /scripts/docs-check.sh: -------------------------------------------------------------------------------- 1 | echo "prev commit: $CACHED_COMMIT_REF" 2 | echo "current commit: $COMMIT_REF" 3 | git diff --quiet $CACHED_COMMIT_REF $COMMIT_REF docs package.json pnpm-lock.yaml netlify.toml scripts/docs-check.sh 4 | has_diff=$? 5 | git describe --exact-match --tags $(git log -n1 --pretty='%h') 6 | has_tag=$? 7 | 8 | echo "diff exit code: $has_diff" 9 | echo "tag exit code: $has_tag" 10 | 11 | exit $(($has_diff || $has_tag)) -------------------------------------------------------------------------------- /scripts/vitestGlobalSetup.ts: -------------------------------------------------------------------------------- 1 | import { rmSync } from 'node:fs' 2 | import fs from 'node:fs/promises' 3 | import path from 'node:path' 4 | 5 | const tempRuntimePath = path.resolve( 6 | __dirname, 7 | '../packages/vite-plugin-checker/src/@runtime', 8 | ) 9 | 10 | export async function setup(): Promise { 11 | await fs.rm(tempRuntimePath, { force: true, recursive: true }) 12 | await fs.mkdir(tempRuntimePath, { recursive: true }) 13 | await fs.cp( 14 | path.resolve(__dirname, '../packages/vite-plugin-checker/dist/@runtime'), 15 | tempRuntimePath, 16 | { 17 | recursive: true, 18 | dereference: false, 19 | }, 20 | ) 21 | } 22 | 23 | export async function teardown(): Promise { 24 | rmSync(tempRuntimePath, { force: true, recursive: true }) 25 | } 26 | -------------------------------------------------------------------------------- /vitest.config.e2e.ts: -------------------------------------------------------------------------------- 1 | import { resolve } from 'node:path' 2 | import { defineConfig } from 'vitest/config' 3 | 4 | const timeout = process.env.CI ? 80000 : 40000 5 | 6 | export default defineConfig({ 7 | test: { 8 | cache: false, 9 | pool: 'forks', 10 | poolOptions: { 11 | forks: { 12 | // singleFork: true, 13 | }, 14 | }, 15 | include: ['./playground/**/*.spec.[tj]s'], 16 | setupFiles: ['./playground/vitestSetup.ts'], 17 | globalSetup: ['./playground/vitestGlobalSetup.ts'], 18 | testTimeout: timeout, 19 | hookTimeout: timeout, 20 | globals: true, 21 | reporters: 'dot', 22 | onConsoleLog(log) { 23 | if (log.match(/experimental|jit engine|emitted file|tailwind/i)) 24 | return false 25 | }, 26 | }, 27 | esbuild: { 28 | target: 'node14', 29 | }, 30 | }) 31 | -------------------------------------------------------------------------------- /vitest.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vitest/config' 2 | 3 | export default defineConfig({ 4 | test: { 5 | exclude: [ 6 | '**/node_modules/**', 7 | '**/dist/**', 8 | './playground/**/*.*', 9 | './playground-temp/**/*.*', 10 | ], 11 | testTimeout: 20000, 12 | globalSetup: ['./scripts/vitestGlobalSetup.ts'], 13 | }, 14 | esbuild: { 15 | target: 'node14', 16 | }, 17 | }) 18 | --------------------------------------------------------------------------------