├── .editorconfig
├── .git-blame-ignore-revs
├── .gitattributes
├── .github
├── ISSUE_TEMPLATE
│ ├── bug_report.yml
│ ├── config.yml
│ └── feature_request.yml
├── PULL_REQUEST_TEMPLATE.md
├── renovate.json5
└── workflows
│ ├── ci.yml
│ ├── issue-close-require.yml
│ ├── issue-labeled.yml
│ ├── lock-closed-issues.yml
│ ├── publish.yml
│ ├── release-continuous.yml
│ ├── release-tag.yml
│ └── semantic-pull-request.yml
├── .gitignore
├── .npmrc
├── .prettierignore
├── .prettierrc.json
├── CODE_OF_CONDUCT.md
├── LICENSE
├── README.md
├── eslint.config.js
├── package.json
├── packages
├── plugin-vue-jsx
│ ├── CHANGELOG.md
│ ├── LICENSE
│ ├── README.md
│ ├── build.config.ts
│ ├── package.json
│ ├── src
│ │ ├── index.ts
│ │ └── types.ts
│ └── tsconfig.json
└── plugin-vue
│ ├── CHANGELOG.md
│ ├── LICENSE
│ ├── README.md
│ ├── build.config.ts
│ ├── package.json
│ ├── src
│ ├── compiler.ts
│ ├── handleHotUpdate.ts
│ ├── helper.ts
│ ├── index.ts
│ ├── main.ts
│ ├── script.ts
│ ├── style.ts
│ ├── template.ts
│ └── utils
│ │ ├── descriptorCache.ts
│ │ ├── error.ts
│ │ └── query.ts
│ └── tsconfig.json
├── playground
├── package.json
├── shims.d.ts
├── ssr-vue
│ ├── __tests__
│ │ ├── fixtures
│ │ │ └── ssrModuleLoader-bad.js
│ │ ├── serve.ts
│ │ └── ssr-vue.spec.ts
│ ├── dep-import-type
│ │ ├── deep
│ │ │ └── index.d.ts
│ │ └── package.json
│ ├── example-external-component
│ │ ├── ExampleExternalComponent.vue
│ │ ├── index.js
│ │ └── package.json
│ ├── index.html
│ ├── package.json
│ ├── prerender.js
│ ├── server.js
│ ├── src
│ │ ├── App.vue
│ │ ├── assets
│ │ │ ├── button.css
│ │ │ ├── fonts
│ │ │ │ ├── Inter-Italic.woff
│ │ │ │ └── Inter-Italic.woff2
│ │ │ └── logo.png
│ │ ├── components
│ │ │ ├── Foo.jsx
│ │ │ ├── ImportType.vue
│ │ │ ├── button.js
│ │ │ └── foo.css
│ │ ├── entry-client.js
│ │ ├── entry-server.js
│ │ ├── main.js
│ │ ├── pages
│ │ │ ├── About.vue
│ │ │ ├── External.vue
│ │ │ ├── Home.vue
│ │ │ └── Store.vue
│ │ └── router.js
│ ├── vite.config.js
│ └── vite.config.noexternal.js
├── tailwind-v3
│ ├── App.vue
│ ├── PugTemplate.vue
│ ├── __tests__
│ │ └── tailwind.spec.ts
│ ├── index.css
│ ├── index.html
│ ├── package.json
│ ├── postcss.config.cts
│ ├── tailwind.config.js
│ └── vite.config.ts
├── tailwind
│ ├── App.vue
│ ├── PugTemplate.vue
│ ├── __tests__
│ │ └── tailwind.spec.ts
│ ├── index.css
│ ├── index.html
│ ├── package.json
│ └── vite.config.ts
├── test-utils.ts
├── tsconfig.json
├── vitestGlobalSetup.ts
├── vitestSetup.ts
├── vue-asset-base
│ ├── Main.vue
│ ├── __tests__
│ │ └── vue-asset-base.spec.ts
│ ├── assets
│ │ └── asset.png
│ ├── env.d.ts
│ ├── index.html
│ ├── package.json
│ └── vite.config.ts
├── vue-custom-id
│ ├── Main.vue
│ ├── __tests__
│ │ └── vue-custom-id.spec.ts
│ ├── components
│ │ └── Foo.vue
│ ├── index.html
│ ├── package.json
│ └── vite.config.ts
├── vue-external
│ └── src-import
│ │ ├── SrcImport.vue
│ │ ├── css.module.css
│ │ ├── script.ts
│ │ ├── srcImportModuleStyle.vue
│ │ ├── srcImportModuleStyle2.vue
│ │ ├── srcImportStyle.vue
│ │ ├── srcImportStyle2.vue
│ │ ├── style.css
│ │ ├── style2.css
│ │ └── template.html
├── vue-jsx
│ ├── Comp.tsx
│ ├── Comps.jsx
│ ├── OtherExt.tesx
│ ├── Query.jsx
│ ├── Script.vue
│ ├── SrcImport.jsx
│ ├── SrcImport.vue
│ ├── TsImport.vue
│ ├── TsImportFile.ts
│ ├── __tests__
│ │ └── vue-jsx.spec.ts
│ ├── index.html
│ ├── main.jsx
│ ├── package.json
│ ├── setup-syntax-jsx.vue
│ └── vite.config.js
├── vue-legacy
│ ├── Main.vue
│ ├── __tests__
│ │ └── vue-legacy.spec.ts
│ ├── assets
│ │ └── asset.png
│ ├── env.d.ts
│ ├── index.html
│ ├── inline.css
│ ├── module.vue
│ ├── package.json
│ └── vite.config.ts
├── vue-lib
│ ├── __tests__
│ │ ├── serve.ts
│ │ └── vue-lib.spec.ts
│ ├── index.html
│ ├── package.json
│ ├── src-consumer
│ │ └── index.ts
│ ├── src-lib-css
│ │ ├── index.css
│ │ └── index.ts
│ ├── src-lib
│ │ ├── CompA.vue
│ │ ├── CompB.vue
│ │ └── index.ts
│ ├── vite.config.consumer.ts
│ ├── vite.config.lib-css.ts
│ └── vite.config.lib.ts
├── vue-server-origin
│ ├── Main.vue
│ ├── __tests__
│ │ └── vue-server-origin.spec.ts
│ ├── assets
│ │ └── asset.png
│ ├── env.d.ts
│ ├── index.html
│ ├── package.json
│ └── vite.config.ts
├── vue-sourcemap
│ ├── Css.vue
│ ├── EmptyScript.vue
│ ├── Js.vue
│ ├── Less.vue
│ ├── Main.vue
│ ├── NoScript.vue
│ ├── NoTemplate.vue
│ ├── Sass.vue
│ ├── SassWithImport.vue
│ ├── Ts.vue
│ ├── __tests__
│ │ ├── __snapshots__
│ │ │ └── vue-sourcemap.spec.ts.snap
│ │ └── vue-sourcemap.spec.ts
│ ├── index.html
│ ├── package.json
│ ├── postcss.config.js
│ ├── sassWithImportImported.sass
│ ├── src-import
│ │ ├── SrcImport.vue
│ │ ├── src-import-imported.sass
│ │ ├── src-import.css
│ │ └── src-import.sass
│ └── vite.config.ts
└── vue
│ ├── Assets.vue
│ ├── AsyncComponent.vue
│ ├── CssModules.vue
│ ├── CustomBlock.vue
│ ├── CustomBlockPlugin.ts
│ ├── CustomElement.ce.vue
│ ├── DefaultLangs.vue
│ ├── ExportTypeProps1.vue
│ ├── ExportTypeProps2.vue
│ ├── Hmr.vue
│ ├── HmrCircularReference.vue
│ ├── HmrCircularReferenceFile.d.ts
│ ├── HmrTsx.vue
│ ├── Main.vue
│ ├── Node.vue
│ ├── Null.vue
│ ├── ParserOptions.vue
│ ├── PreProcessors.vue
│ ├── PreProcessorsHmr.vue
│ ├── ScanDep.vue
│ ├── Slotted.vue
│ ├── Syntax.vue
│ ├── TreeShakeScopedStyle.vue
│ ├── TsGeneric.vue
│ ├── TsImport.vue
│ ├── TsImportFile.ts
│ ├── TypeProps.vue
│ ├── TypePropsTsx.vue
│ ├── Url.vue
│ ├── __tests__
│ └── vue.spec.ts
│ ├── assets
│ ├── asset.png
│ └── fragment.svg
│ ├── index.html
│ ├── lib.js
│ ├── package.json
│ ├── pre-compiled
│ ├── external-cssmodules.vue.js
│ ├── external-scoped.vue.js
│ ├── external.css
│ ├── external.module.css
│ ├── foo.vue.js
│ └── foo.vue__0.css
│ ├── public
│ ├── favicon.ico
│ └── icon.png
│ ├── setup-import-template
│ ├── SetupImportTemplate.vue
│ └── template.html
│ ├── src-import
│ ├── SrcImport.vue
│ ├── css.module.css
│ ├── script.ts
│ ├── srcImportModuleStyle.vue
│ ├── srcImportModuleStyle2.vue
│ ├── srcImportStyle.vue
│ ├── srcImportStyle2.vue
│ ├── style.css
│ ├── style2.css
│ └── template.html
│ ├── tsconfig.json
│ ├── types-aliased.d.ts
│ ├── types.ts
│ ├── vite-env.d.ts
│ ├── vite.config.ts
│ ├── worker.vue
│ └── workerTest.js
├── pnpm-lock.yaml
├── pnpm-workspace.yaml
├── scripts
├── patchCJS.ts
├── publishCI.ts
├── release.ts
├── releaseUtils.ts
└── tsconfig.json
├── vitest.config.e2e.ts
└── vitest.config.ts
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | charset = utf-8
5 | indent_style = space
6 | indent_size = 2
7 | end_of_line = lf
8 | insert_final_newline = true
9 | trim_trailing_whitespace = true
10 |
--------------------------------------------------------------------------------
/.git-blame-ignore-revs:
--------------------------------------------------------------------------------
1 | # chore: enable prettier trailing commas (#37)
2 | eef8929c95d8b5cce1385a1d5e60da56a8420c0b
3 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | * text=auto eol=lf
2 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.yml:
--------------------------------------------------------------------------------
1 | name: "\U0001F41E Bug report"
2 | description: Report an issue
3 | labels: [pending triage]
4 | type: Bug
5 | body:
6 | - type: markdown
7 | attributes:
8 | value: |
9 | Thanks for taking the time to fill out this bug report!
10 | - type: checkboxes
11 | id: plugins
12 | attributes:
13 | label: Related plugins
14 | description: Select the plugin which is related
15 | options:
16 | - label: |
17 | [plugin-vue](https://github.com/vitejs/vite-plugin-vue/tree/main/packages/plugin-vue)
18 | - label: |
19 | [plugin-vue-jsx](https://github.com/vitejs/vite-plugin-vue/tree/main/packages/plugin-vue-jsx)
20 | - type: textarea
21 | id: bug-description
22 | attributes:
23 | label: Describe the bug
24 | description: A clear and concise description of what the bug is. If you intend to submit a PR for this issue, tell us in the description. Thanks!
25 | placeholder: I am doing ... What I expect is ... What actually happening is ...
26 | validations:
27 | required: true
28 | - type: input
29 | id: reproduction
30 | attributes:
31 | label: Reproduction
32 | description: Please provide a link via [vite.new/vue](https://vite.new/vue) / [vite.new/vue-ts](https://vite.new/vue-ts) or a link to a repo that can reproduce the problem you ran into. `npm create vite@latest` and `npm create vite-extra@latest` (for SSR or library repros) can be used as a starter template. A [minimal reproduction](https://stackoverflow.com/help/minimal-reproducible-example) is required ([Why?](https://antfu.me/posts/why-reproductions-are-required)). If a report is vague (e.g. just a generic error message) and has no reproduction, it will receive a "need reproduction" label. If no reproduction is provided after 3 days, it will be auto-closed.
33 | placeholder: Reproduction URL
34 | validations:
35 | required: true
36 | - type: textarea
37 | id: reproduction-steps
38 | attributes:
39 | label: Steps to reproduce
40 | description: Please provide any reproduction steps that may need to be described. E.g. if it happens only when running the dev or build script make sure it's clear which one to use.
41 | placeholder: Run `npm install` followed by `npm run dev`
42 | - type: textarea
43 | id: system-info
44 | attributes:
45 | label: System Info
46 | description: Output of `npx envinfo --system --npmPackages '{vite,@vitejs/*}' --binaries --browsers`
47 | render: shell
48 | placeholder: System, Binaries, Browsers
49 | validations:
50 | required: true
51 | - type: dropdown
52 | id: package-manager
53 | attributes:
54 | label: Used Package Manager
55 | description: Select the used package manager
56 | options:
57 | - npm
58 | - yarn
59 | - pnpm
60 | validations:
61 | required: true
62 | - type: textarea
63 | id: logs
64 | attributes:
65 | label: Logs
66 | description: |
67 | Optional if provided reproduction. Please try not to insert an image but copy paste the log text.
68 |
69 | 1. Run `vite` or `vite build` with the `--debug` flag.
70 | 2. Provide the error log here in the format below.
71 |
72 | ````
73 |
74 | Click to expand!
75 |
76 | ```shell
77 | // paste the log text here
78 | ```
79 |
80 | ````
81 | - type: checkboxes
82 | id: checkboxes
83 | attributes:
84 | label: Validations
85 | description: Before submitting the issue, please make sure you do the following
86 | options:
87 | - label: Follow our [Code of Conduct](https://github.com/vitejs/vite/blob/main/CODE_OF_CONDUCT.md)
88 | required: true
89 | - label: Read the [Contributing Guidelines](https://github.com/vitejs/vite/blob/main/CONTRIBUTING.md).
90 | required: true
91 | - label: Read the [docs](https://vitejs.dev/guide).
92 | required: true
93 | - label: Check that there isn't [already an issue](https://github.com/vitejs/vite-plugin-vue/issues) that reports the same bug to avoid creating a duplicate.
94 | required: true
95 | - label: Make sure this is a Vite issue and not a framework-specific issue. For example, if it's a Vue SFC related bug, it should likely be reported to [vuejs/core](https://github.com/vuejs/core) instead.
96 | required: true
97 | - label: Check that this is a concrete bug. For Q&A open a [GitHub Discussion](https://github.com/vitejs/vite-plugin-vue/discussions) or join our [Discord Chat Server](https://chat.vitejs.dev/).
98 | required: true
99 | - label: The provided reproduction is a [minimal reproducible example](https://stackoverflow.com/help/minimal-reproducible-example) of the bug.
100 | required: true
101 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/config.yml:
--------------------------------------------------------------------------------
1 | blank_issues_enabled: false
2 | contact_links:
3 | - name: Discord Chat
4 | url: https://chat.vitejs.dev
5 | about: Ask questions and discuss with other Vite users in real time.
6 | - name: Questions & Discussions
7 | url: https://github.com/vitejs/vite-plugin-vue/discussions
8 | about: Use GitHub discussions for message-board style questions and discussions.
9 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.yml:
--------------------------------------------------------------------------------
1 | name: "\U0001F680 New feature proposal"
2 | description: Propose a new feature
3 | labels: ["pending triage"]
4 | type: Feature
5 | body:
6 | - type: markdown
7 | attributes:
8 | value: |
9 | Thanks for your interest in the project and taking the time to fill out this feature report!
10 | - type: checkboxes
11 | id: plugins
12 | attributes:
13 | label: Related plugins
14 | description: Select the plugin which is related
15 | options:
16 | - label: |
17 | [plugin-vue](https://github.com/vitejs/vite-plugin-vue/tree/main/packages/plugin-vue)
18 | - label: |
19 | [plugin-vue-jsx](https://github.com/vitejs/vite-plugin-vue/tree/main/packages/plugin-vue-jsx)
20 | - type: textarea
21 | id: feature-description
22 | attributes:
23 | label: Description
24 | description: "Clear and concise description of the problem. Please make the reason and usecases as detailed as possible. If you intend to submit a PR for this issue, tell us in the description. Thanks!"
25 | placeholder: As a developer using Vite I want [goal / wish] so that [benefit].
26 | validations:
27 | required: true
28 | - type: textarea
29 | id: suggested-solution
30 | attributes:
31 | label: Suggested solution
32 | description: "In module [xy] we could provide following implementation..."
33 | validations:
34 | required: true
35 | - type: textarea
36 | id: alternative
37 | attributes:
38 | label: Alternative
39 | description: Clear and concise description of any alternative solutions or features you've considered.
40 | - type: textarea
41 | id: additional-context
42 | attributes:
43 | label: Additional context
44 | description: Any other context or screenshots about the feature request here.
45 | - type: checkboxes
46 | id: checkboxes
47 | attributes:
48 | label: Validations
49 | description: Before submitting the issue, please make sure you do the following
50 | options:
51 | - label: Follow our [Code of Conduct](https://github.com/vitejs/vite/blob/main/CODE_OF_CONDUCT.md)
52 | required: true
53 | - label: Read the [Contributing Guidelines](https://github.com/vitejs/vite/blob/main/CONTRIBUTING.md).
54 | required: true
55 | - label: Read the [docs](https://vitejs.dev/guide).
56 | required: true
57 | - label: Check that there isn't already an issue that request the same feature to avoid creating a duplicate.
58 | required: true
59 |
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | ### Description
4 |
5 |
6 |
7 | ### Additional context
8 |
9 |
10 |
11 | ---
12 |
13 | ### What is the purpose of this pull request?
14 |
15 | - [ ] Bug fix
16 | - [ ] New Feature
17 | - [ ] Documentation update
18 | - [ ] Other
19 |
20 | ### Before submitting the PR, please make sure you do the following
21 |
22 | - [ ] Read the [Contributing Guidelines](https://github.com/vitejs/vite/blob/main/CONTRIBUTING.md).
23 | - [ ] Read the [Pull Request Guidelines](https://github.com/vitejs/vite/blob/main/CONTRIBUTING.md#pull-request-guidelines) and follow the [PR Title Convention](https://github.com/vitejs/vite/blob/main/.github/commit-convention.md).
24 | - [ ] Check that there isn't already a PR that solves the problem the same way to avoid creating a duplicate.
25 | - [ ] Provide a description in this PR that addresses **what** the PR is solving, or reference the issue that it solves (e.g. `fixes #123`).
26 | - [ ] Ideally, include relevant tests that fail without this PR but pass with it.
27 |
--------------------------------------------------------------------------------
/.github/renovate.json5:
--------------------------------------------------------------------------------
1 | {
2 | "extends": ["config:base", "schedule:weekly", "group:allNonMajor"],
3 | "labels": ["dependencies"],
4 | "ignorePaths": ["**/__tests__/**"],
5 | "pin": false,
6 | "rangeStrategy": "bump",
7 | "packageRules": [
8 | {
9 | "depTypeList": ["peerDependencies"],
10 | "enabled": false,
11 | },
12 | {
13 | groupName: "upstream",
14 | matchPackageNames: ["vite"],
15 | matchPackagePrefixes: ["rollup", "@rollup", "@vitejs"],
16 | },
17 | {
18 | groupName: "prettier",
19 | matchPackageNames: ["prettier"],
20 | },
21 | {
22 | "matchDepTypes": ["action"],
23 | "excludePackagePrefixes": ["actions/", "github/"],
24 | "pinDigests": true,
25 | },
26 | ],
27 | "ignoreDeps": [
28 | // manually bumping
29 | "node",
30 |
31 | // breaking changes
32 | "kill-port", // `kill-port:^2.0.0 has perf issues (#8392)
33 | ],
34 | }
35 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | name: CI
2 |
3 | env:
4 | # 7 GiB by default on GitHub, setting to 6 GiB
5 | # https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners#supported-runners-and-hardware-resources
6 | NODE_OPTIONS: --max-old-space-size=6144
7 | # install playwright binary manually (because pnpm only runs install script once)
8 | PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: "1"
9 | # Vitest auto retry on flaky segfault
10 | VITEST_SEGFAULT_RETRY: 3
11 |
12 | permissions: {}
13 |
14 | on:
15 | push:
16 | branches:
17 | - main
18 | - release/*
19 | - feat/*
20 | - fix/*
21 | - perf/*
22 | - v1
23 | - v2
24 | - v2.*
25 | - v3.*
26 | pull_request:
27 | workflow_dispatch:
28 |
29 | concurrency:
30 | group: ${{ github.workflow }}-${{ github.event.number || github.sha }}
31 | cancel-in-progress: true
32 |
33 | jobs:
34 | build:
35 | timeout-minutes: 20
36 | runs-on: ${{ matrix.os }}
37 | strategy:
38 | matrix:
39 | os: [ubuntu-latest]
40 | node_version: [18, 20]
41 | include:
42 | # Active LTS + other OS
43 | - os: macos-latest
44 | node_version: 20
45 | - os: windows-latest
46 | node_version: 20
47 | fail-fast: false
48 |
49 | name: "Build&Test: node-${{ matrix.node_version }}, ${{ matrix.os }}"
50 | steps:
51 | - name: Checkout
52 | uses: actions/checkout@v4
53 |
54 | - name: Install pnpm
55 | uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0
56 |
57 | - name: Set node version to ${{ matrix.node_version }}
58 | uses: actions/setup-node@v4
59 | with:
60 | node-version: ${{ matrix.node_version }}
61 | cache: "pnpm"
62 |
63 | - name: Install deps
64 | run: pnpm install
65 |
66 | # Install playwright's binary under custom directory to cache
67 | - name: Set Playwright path (non-windows)
68 | if: runner.os != 'Windows'
69 | run: echo "PLAYWRIGHT_BROWSERS_PATH=$HOME/.cache/playwright-bin" >> $GITHUB_ENV
70 | - name: Set Playwright path (windows)
71 | if: runner.os == 'Windows'
72 | run: echo "PLAYWRIGHT_BROWSERS_PATH=$HOME\.cache\playwright-bin" >> $env:GITHUB_ENV
73 |
74 | - name: Cache Playwright's binary
75 | uses: actions/cache@v4
76 | with:
77 | # Playwright removes unused browsers automatically
78 | # So does not need to add playwright version to key
79 | key: ${{ runner.os }}-playwright-bin-v1
80 | path: ${{ env.PLAYWRIGHT_BROWSERS_PATH }}
81 |
82 | - name: Install Playwright
83 | # does not need to explicitly set chromium after https://github.com/microsoft/playwright/issues/14862 is solved
84 | run: pnpm playwright install chromium
85 |
86 | - name: Build
87 | run: pnpm run build
88 |
89 | - name: Test serve
90 | run: pnpm run test-serve
91 |
92 | - name: Test build
93 | run: pnpm run test-build
94 |
95 | lint:
96 | if: github.repository == 'vitejs/vite-plugin-vue'
97 | timeout-minutes: 10
98 | runs-on: ubuntu-latest
99 | name: "Lint: node-LTS, ubuntu-latest"
100 | steps:
101 | - uses: actions/checkout@v4
102 | with:
103 | fetch-depth: 0
104 |
105 | - name: Install pnpm
106 | uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0
107 |
108 | - name: Set node version to LTS
109 | uses: actions/setup-node@v4
110 | with:
111 | node-version: lts/*
112 | cache: "pnpm"
113 |
114 | - name: Install deps
115 | run: pnpm install
116 |
117 | - name: Build
118 | run: pnpm run build
119 |
120 | - name: Lint
121 | run: pnpm run lint
122 |
123 | - name: Check formatting
124 | run: pnpm prettier --check .
125 |
126 | - name: Typecheck
127 | run: pnpm run typecheck
128 |
--------------------------------------------------------------------------------
/.github/workflows/issue-close-require.yml:
--------------------------------------------------------------------------------
1 | name: Issue Close Require
2 |
3 | on:
4 | schedule:
5 | - cron: "0 0 * * *"
6 |
7 | jobs:
8 | close-issues:
9 | if: github.repository == 'vitejs/vite-plugin-vue'
10 | runs-on: ubuntu-latest
11 | permissions:
12 | issues: write # for actions-cool/issues-helper to update issues
13 | pull-requests: write # for actions-cool/issues-helper to update PRs
14 | steps:
15 | - name: need reproduction
16 | uses: actions-cool/issues-helper@a610082f8ac0cf03e357eb8dd0d5e2ba075e017e # v3
17 | with:
18 | actions: "close-issues"
19 | token: ${{ secrets.GITHUB_TOKEN }}
20 | labels: "need reproduction"
21 | inactive-day: 3
22 |
--------------------------------------------------------------------------------
/.github/workflows/issue-labeled.yml:
--------------------------------------------------------------------------------
1 | name: Issue Labeled
2 |
3 | on:
4 | issues:
5 | types: [labeled]
6 |
7 | jobs:
8 | reply-labeled:
9 | if: github.repository == 'vitejs/vite-plugin-vue'
10 | runs-on: ubuntu-latest
11 | permissions:
12 | issues: write # for actions-cool/issues-helper to update issues
13 | pull-requests: write # for actions-cool/issues-helper to update PRs
14 | steps:
15 | - name: contribution welcome
16 | if: github.event.label.name == 'contribution welcome' || github.event.label.name == 'help wanted'
17 | uses: actions-cool/issues-helper@a610082f8ac0cf03e357eb8dd0d5e2ba075e017e # v3
18 | with:
19 | actions: "create-comment, remove-labels"
20 | token: ${{ secrets.GITHUB_TOKEN }}
21 | issue-number: ${{ github.event.issue.number }}
22 | body: |
23 | Hello @${{ github.event.issue.user.login }}. We like your proposal/feedback and would appreciate a contribution via a Pull Request by you or another community member. We thank you in advance for your contribution and are looking forward to reviewing it!
24 | labels: "pending triage, need reproduction"
25 |
26 | - name: remove pending
27 | if: (github.event.label.name == 'enhancement' || contains(github.event.label.description, '(priority)')) && contains(github.event.issue.labels.*.name, 'pending triage')
28 | uses: actions-cool/issues-helper@a610082f8ac0cf03e357eb8dd0d5e2ba075e017e # v3
29 | with:
30 | actions: "remove-labels"
31 | token: ${{ secrets.GITHUB_TOKEN }}
32 | issue-number: ${{ github.event.issue.number }}
33 | labels: "pending triage"
34 |
35 | - name: need reproduction
36 | if: github.event.label.name == 'need reproduction'
37 | uses: actions-cool/issues-helper@a610082f8ac0cf03e357eb8dd0d5e2ba075e017e # v3
38 | with:
39 | actions: "create-comment, remove-labels"
40 | token: ${{ secrets.GITHUB_TOKEN }}
41 | issue-number: ${{ github.event.issue.number }}
42 | body: |
43 | Hello @${{ github.event.issue.user.login }}. Please provide a [minimal reproduction](https://stackoverflow.com/help/minimal-reproducible-example) using a GitHub repository or [StackBlitz](https://vite.new). Issues marked with `need reproduction` will be closed if they have no activity within 3 days.
44 | labels: "pending triage"
45 |
--------------------------------------------------------------------------------
/.github/workflows/lock-closed-issues.yml:
--------------------------------------------------------------------------------
1 | name: Lock Closed Issues
2 |
3 | on:
4 | schedule:
5 | - cron: "0 0 * * *"
6 |
7 | permissions:
8 | issues: write
9 |
10 | jobs:
11 | action:
12 | if: github.repository == 'vitejs/vite-plugin-vue'
13 | runs-on: ubuntu-latest
14 | steps:
15 | - uses: dessant/lock-threads@1bf7ec25051fe7c00bdd17e6a7cf3d7bfb7dc771 # v5
16 | with:
17 | github-token: ${{ secrets.GITHUB_TOKEN }}
18 | issue-inactive-days: "14"
19 | #issue-comment: |
20 | # This issue has been locked since it has been closed for more than 14 days.
21 | #
22 | # If you have found a concrete bug or regression related to it, please open a new [bug report](https://github.com/vitejs/vite-plugin-vue/issues/new/choose) with a reproduction against the latest Vite version. If you have any other comments you should join the chat at [Vite Land](https://chat.vitejs.dev) or create a new [discussion](https://github.com/vitejs/vite-plugin-vue/discussions).
23 | issue-lock-reason: ""
24 | process-only: "issues"
25 |
--------------------------------------------------------------------------------
/.github/workflows/publish.yml:
--------------------------------------------------------------------------------
1 | name: Publish Package
2 |
3 | on:
4 | push:
5 | tags:
6 | - "v*" # Push events to matching v*, i.e. v1.0, v20.15.10
7 | - "plugin-*" # Push events to matching plugin-*, i.e. plugin-(vue|vue-jsx|react|legacy)@1.0.0
8 | - "create-vite*" # # Push events to matching create-vite*, i.e. create-vite@1.0.0
9 |
10 | jobs:
11 | publish:
12 | # prevents this action from running on forks
13 | if: github.repository == 'vitejs/vite-plugin-vue'
14 | runs-on: ubuntu-latest
15 | permissions:
16 | contents: read
17 | id-token: write
18 | environment: Release
19 | steps:
20 | - name: Checkout
21 | uses: actions/checkout@v4
22 |
23 | - name: Install pnpm
24 | uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0
25 |
26 | - name: Set node version to LTS
27 | uses: actions/setup-node@v4
28 | with:
29 | node-version: lts/*
30 | registry-url: https://registry.npmjs.org/
31 | cache: "pnpm"
32 |
33 | - name: Install deps
34 | run: pnpm install
35 | env:
36 | PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: "1"
37 |
38 | # https://github.com/npm/cli/pull/6978
39 | - name: Pin npm
40 | run: npm i -g npm@10.2.4
41 |
42 | - name: Publish package
43 | run: pnpm run ci-publish ${{ github.ref_name }}
44 | env:
45 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
46 |
--------------------------------------------------------------------------------
/.github/workflows/release-continuous.yml:
--------------------------------------------------------------------------------
1 | name: Publish Any Commit
2 | on: [push, pull_request]
3 |
4 | permissions: {}
5 |
6 | jobs:
7 | build:
8 | runs-on: ubuntu-latest
9 |
10 | steps:
11 | - name: Checkout code
12 | uses: actions/checkout@v4
13 |
14 | - name: Install pnpm
15 | uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0
16 |
17 | - uses: actions/setup-node@v4
18 | with:
19 | node-version: lts/*
20 | cache: pnpm
21 |
22 | - name: Install dependencies
23 | run: pnpm install
24 |
25 | - name: Build
26 | run: pnpm build
27 |
28 | - name: Publish
29 | run: pnpm dlx pkg-pr-new@0.0 publish --pnpm --compact './packages/*'
30 |
--------------------------------------------------------------------------------
/.github/workflows/release-tag.yml:
--------------------------------------------------------------------------------
1 | name: Add GitHub Release Tag
2 |
3 | on:
4 | push:
5 | tags:
6 | - "v*" # Push events to matching v*, i.e. v1.0, v20.15.10
7 | - "plugin-*" # Push events to matching plugin-*, i.e. plugin-(vue|vue-jsx|react|legacy)@1.0.0
8 | - "create-vite*" # # Push events to matching create-vite*, i.e. create-vite@1.0.0
9 |
10 | # $GITHUB_REF_NAME - https://docs.github.com/en/actions/learn-github-actions/environment-variables#default-environment-variables
11 |
12 | jobs:
13 | release:
14 | if: github.repository == 'vitejs/vite-plugin-vue'
15 | runs-on: ubuntu-latest
16 | permissions:
17 | contents: write # for yyx990803/release-tag to create a release tag
18 | steps:
19 | - uses: actions/checkout@v4
20 |
21 | - name: Get pkgName for tag
22 | id: tag
23 | run: |
24 | # matching v2.0.0 / v2.0.0-beta.8 etc
25 | if [[ $GITHUB_REF_NAME =~ ^v.+ ]]; then
26 | pkgName="vite"
27 | else
28 | # `%@*` truncates @ and version number from the right side.
29 | # https://stackoverflow.com/questions/9532654/expression-after-last-specific-character
30 | pkgName=${GITHUB_REF_NAME%@*}
31 | fi
32 |
33 | echo "::set-output name=pkgName::$pkgName"
34 |
35 | - name: Create Release for Tag
36 | id: release_tag
37 | uses: yyx990803/release-tag@master
38 | env:
39 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
40 | with:
41 | tag_name: ${{ github.ref }}
42 | body: |
43 | Please refer to [CHANGELOG.md](https://github.com/vitejs/vite-plugin-vue/blob/${{ github.ref_name }}/packages/${{ steps.tag.outputs.pkgName }}/CHANGELOG.md) for details.
44 |
--------------------------------------------------------------------------------
/.github/workflows/semantic-pull-request.yml:
--------------------------------------------------------------------------------
1 | name: Semantic Pull Request
2 |
3 | on:
4 | pull_request_target:
5 | types:
6 | - opened
7 | - edited
8 | - synchronize
9 |
10 | jobs:
11 | main:
12 | if: github.repository == 'vitejs/vite-plugin-vue'
13 | runs-on: ubuntu-latest
14 | name: Semantic Pull Request
15 | permissions:
16 | pull-requests: read
17 | steps:
18 | - name: Validate PR title
19 | uses: amannn/action-semantic-pull-request@0723387faaf9b38adef4775cd42cfd5155ed6017 # v5
20 | with:
21 | subjectPattern: ^(?![A-Z]).+$
22 | subjectPatternError: |
23 | The subject "{subject}" found in the pull request title "{title}"
24 | didn't match the configured pattern. Please ensure that the subject
25 | doesn't start with an uppercase character.
26 | env:
27 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
28 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | !**/glob-import/dir/node_modules
2 | .DS_Store
3 | .idea
4 | *.cpuprofile
5 | *.local
6 | *.log
7 | /.vscode/
8 | /packages/vite/LICENSE
9 | dist
10 | dist-ssr
11 | explorations
12 | node_modules
13 | playground-temp
14 | temp
15 | TODOs.md
16 | .eslintcache
17 |
--------------------------------------------------------------------------------
/.npmrc:
--------------------------------------------------------------------------------
1 | hoist-pattern[]=postcss
2 | hoist-pattern[]=pug # playground/vue > @vue/compiler-sfc
3 | hoist-pattern[]=ts-node # playground/tailwind
4 | strict-peer-dependencies=false
5 | shell-emulator=true
6 | auto-install-peers=false
7 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | packages/*/CHANGELOG.md
2 | playground-temp/
3 | dist/
4 | temp/
5 | LICENSE.md
6 | pnpm-lock.yaml
7 | pnpm-workspace.yaml
8 | playground/tsconfig-json-load-error/has-error/tsconfig.json
9 | playground/html/invalid.html
10 | playground/html/valid.html
11 | playground/worker/classic-worker.js
12 |
--------------------------------------------------------------------------------
/.prettierrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "semi": false,
3 | "tabWidth": 2,
4 | "singleQuote": true,
5 | "printWidth": 80,
6 | "trailingComma": "all",
7 | "overrides": [
8 | {
9 | "files": ["*.json5"],
10 | "options": {
11 | "singleQuote": false,
12 | "quoteProps": "preserve"
13 | }
14 | },
15 | {
16 | "files": ["*.yml"],
17 | "options": {
18 | "singleQuote": false
19 | }
20 | }
21 | ]
22 | }
23 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Code Of Conduct
2 |
3 | ## Our Pledge
4 |
5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, political party, or sexual identity and orientation. Note, however, that religion, political party, or other ideological affiliation provide no exemptions for the behavior we outline as unacceptable in this Code of Conduct.
6 |
7 | ## Our Standards
8 |
9 | Examples of behavior that contributes to creating a positive environment include:
10 |
11 | - Using welcoming and inclusive language
12 | - Being respectful of differing viewpoints and experiences
13 | - Gracefully accepting constructive criticism
14 | - Focusing on what is best for the community
15 | - Showing empathy towards other community members
16 |
17 | Examples of unacceptable behavior by participants include:
18 |
19 | - The use of sexualized language or imagery and unwelcome sexual attention or advances
20 | - Trolling, insulting/derogatory comments, and personal or political attacks
21 | - Public or private harassment
22 | - Publishing others' private information, such as a physical or electronic address, without explicit permission
23 | - Other conduct which could reasonably be considered inappropriate in a professional setting
24 |
25 | ## Our Responsibilities
26 |
27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
28 |
29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
30 |
31 | ## Scope
32 |
33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
34 |
35 | ## Enforcement
36 |
37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team by DM at [Vite Land](https://chat.vitejs.dev). All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
38 |
39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
40 |
41 | ## Attribution
42 |
43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
44 |
45 | [homepage]: https://www.contributor-covenant.org
46 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019-present, Yuxi (Evan) You and Vite contributors
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 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 | # Vite Plugin Vue
16 |
17 | See [`@vitejs/plugin-vue` documentation](packages/plugin-vue/README.md) and [`@vitejs/plugin-vue-jsx` documentation](packages/plugin-vue-jsx/README.md)
18 |
19 | ## Packages
20 |
21 | | Package | Version (click for changelogs) |
22 | | ------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------- |
23 | | [@vitejs/plugin-vue](packages/plugin-vue) | [](packages/plugin-vue/CHANGELOG.md) |
24 | | [@vitejs/plugin-vue-jsx](packages/plugin-vue-jsx) | [](packages/plugin-vue-jsx/CHANGELOG.md) |
25 |
26 | ## License
27 |
28 | [MIT](LICENSE).
29 |
--------------------------------------------------------------------------------
/eslint.config.js:
--------------------------------------------------------------------------------
1 | // @ts-check
2 | import { builtinModules } from 'node:module'
3 | import eslint from '@eslint/js'
4 | import tseslint from 'typescript-eslint'
5 | import nodePlugin from 'eslint-plugin-n'
6 | import * as regexpPlugin from 'eslint-plugin-regexp'
7 | import importPlugin, { createNodeResolver } from 'eslint-plugin-import-x'
8 | import { createTypeScriptImportResolver } from 'eslint-import-resolver-typescript'
9 |
10 | export default tseslint.config(
11 | eslint.configs.recommended,
12 | ...tseslint.configs.recommended,
13 | nodePlugin.configs['flat/recommended'],
14 | regexpPlugin.configs['flat/recommended'],
15 | { ignores: ['**/dist', '**/playground-temp', '**/temp'] },
16 | {
17 | plugins: {
18 | import: importPlugin,
19 | },
20 | settings: {
21 | 'import-x/resolver-next': [
22 | createNodeResolver(),
23 | createTypeScriptImportResolver(),
24 | ],
25 | },
26 | rules: {
27 | eqeqeq: ['warn', 'always', { null: 'never' }],
28 | 'no-empty': ['warn', { allowEmptyCatch: true }],
29 | 'no-useless-escape': 'off',
30 | 'prefer-const': ['warn', { destructuring: 'all' }],
31 |
32 | 'n/no-process-exit': 'off',
33 | 'n/no-missing-import': [
34 | 'error',
35 | {
36 | allowModules: ['types', 'estree', 'less', 'sass', 'stylus'],
37 | tryExtensions: ['.ts', '.js', '.jsx', '.tsx', '.d.ts'],
38 | },
39 | ],
40 | 'n/no-missing-require': [
41 | 'error',
42 | {
43 | // for try-catching yarn pnp
44 | allowModules: ['pnpapi', 'vite'],
45 | tryExtensions: ['.ts', '.js', '.jsx', '.tsx', '.d.ts'],
46 | },
47 | ],
48 | 'n/no-extraneous-import': [
49 | 'error',
50 | { allowModules: ['vite', 'less', 'sass', 'vitest', 'unbuild'] },
51 | ],
52 | 'n/no-extraneous-require': ['error', { allowModules: ['vite'] }],
53 | 'n/no-deprecated-api': 'off',
54 | 'n/no-unpublished-import': 'off',
55 | 'n/no-unpublished-require': 'off',
56 | 'n/no-unsupported-features/es-syntax': 'off',
57 |
58 | '@typescript-eslint/ban-ts-comment': 'off', // TODO: we should turn this on in a new PR
59 | '@typescript-eslint/explicit-module-boundary-types': [
60 | 'error',
61 | { allowArgumentsExplicitlyTypedAsAny: true },
62 | ],
63 | '@typescript-eslint/no-empty-function': [
64 | 'error',
65 | { allow: ['arrowFunctions'] },
66 | ],
67 | '@typescript-eslint/no-empty-object-type': 'off',
68 | '@typescript-eslint/no-explicit-any': 'off', // maybe we should turn this on in a new PR
69 | '@typescript-eslint/no-inferrable-types': 'off',
70 | '@typescript-eslint/no-unused-vars': 'off', // maybe we should turn this on in a new PR
71 | '@typescript-eslint/no-require-imports': 'off',
72 | '@typescript-eslint/consistent-type-imports': [
73 | 'error',
74 | { prefer: 'type-imports' },
75 | ],
76 |
77 | 'import/no-nodejs-modules': [
78 | 'error',
79 | { allow: builtinModules.map((mod) => `node:${mod}`) },
80 | ],
81 | 'import/no-duplicates': 'error',
82 | 'import/order': 'error',
83 | 'sort-imports': [
84 | 'error',
85 | {
86 | ignoreCase: false,
87 | ignoreDeclarationSort: true,
88 | ignoreMemberSort: false,
89 | memberSyntaxSortOrder: ['none', 'all', 'multiple', 'single'],
90 | allowSeparatedGroups: false,
91 | },
92 | ],
93 |
94 | 'regexp/no-contradiction-with-assertion': 'error',
95 | },
96 | },
97 | {
98 | files: ['packages/**'],
99 | ignores: ['**/__tests__/**'],
100 | rules: {
101 | 'no-restricted-globals': ['error', 'require', '__dirname', '__filename'],
102 | },
103 | },
104 | {
105 | files: ['**/build.config.ts'],
106 | rules: {
107 | 'no-undef': 'off',
108 | 'n/no-missing-import': 'off',
109 | '@typescript-eslint/explicit-module-boundary-types': 'off',
110 | },
111 | },
112 | {
113 | files: ['playground/**'],
114 | rules: {
115 | 'n/no-extraneous-import': 'off',
116 | 'n/no-extraneous-require': 'off',
117 | 'n/no-missing-import': 'off',
118 | 'n/no-missing-require': 'off',
119 | // engine field doesn't exist in playgrounds
120 | 'n/no-unsupported-features/es-builtins': [
121 | 'error',
122 | {
123 | version: '^18.0.0 || >=20.0.0',
124 | },
125 | ],
126 | 'n/no-unsupported-features/node-builtins': [
127 | 'error',
128 | {
129 | version: '^18.0.0 || >=20.0.0',
130 | },
131 | ],
132 | '@typescript-eslint/explicit-module-boundary-types': 'off',
133 | },
134 | },
135 | {
136 | files: ['playground/**'],
137 | ignores: ['**/__tests__/**'],
138 | rules: {
139 | 'no-undef': 'off',
140 | 'no-empty': 'off',
141 | 'no-constant-condition': 'off',
142 | '@typescript-eslint/no-empty-function': 'off',
143 | },
144 | },
145 | {
146 | name: 'tests',
147 | files: ['**/__tests__/**/*'],
148 | rules: {
149 | 'n/no-extraneous-import': 'off',
150 | 'n/no-unsupported-features/node-builtins': [
151 | 'error',
152 | {
153 | version: '^18.0.0 || >=20.0.0',
154 | allowExperimental: true,
155 | },
156 | ],
157 | },
158 | },
159 | {
160 | files: ['*.js', '*.mjs', '*.cjs'],
161 | rules: {
162 | '@typescript-eslint/explicit-module-boundary-types': 'off',
163 | },
164 | },
165 | {
166 | files: ['*.d.ts'],
167 | rules: {
168 | '@typescript-eslint/triple-slash-reference': 'off',
169 | },
170 | },
171 | )
172 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@vitejs/vite-plugin-vue-monorepo",
3 | "private": true,
4 | "type": "module",
5 | "engines": {
6 | "node": "^18.0.0 || >=20.0.0"
7 | },
8 | "homepage": "https://github.com/vitejs/vite-plugin-vue/",
9 | "repository": {
10 | "type": "git",
11 | "url": "git+https://github.com/vitejs/vite-plugin-vue.git"
12 | },
13 | "keywords": [
14 | "frontend",
15 | "hmr",
16 | "dev-server",
17 | "build-tool",
18 | "vite",
19 | "vue"
20 | ],
21 | "scripts": {
22 | "preinstall": "npx only-allow pnpm",
23 | "postinstall": "simple-git-hooks",
24 | "format": "prettier --write --cache .",
25 | "lint": "eslint --cache .",
26 | "typecheck": "tsc -p scripts --noEmit && tsc -p playground --noEmit",
27 | "test": "pnpm test-serve && pnpm test-build",
28 | "test-serve": "vitest run -c vitest.config.e2e.ts",
29 | "test-build": "VITE_TEST_BUILD=1 vitest run -c vitest.config.e2e.ts",
30 | "test-build-without-plugin-commonjs": "VITE_TEST_WITHOUT_PLUGIN_COMMONJS=1 pnpm test-build",
31 | "debug-serve": "VITE_DEBUG_SERVE=1 vitest run -c vitest.config.e2e.ts",
32 | "debug-build": "VITE_TEST_BUILD=1 VITE_PRESERVE_BUILD_ARTIFACTS=1 vitest run -c vitest.config.e2e.ts",
33 | "build": "pnpm -r --filter='./packages/*' run build",
34 | "dev": "pnpm -r --parallel --filter='./packages/*' run dev",
35 | "release": "tsx scripts/release.ts",
36 | "ci-publish": "tsx scripts/publishCI.ts"
37 | },
38 | "devDependencies": {
39 | "@babel/types": "^7.27.1",
40 | "@eslint/js": "^9.27.0",
41 | "@types/babel__core": "^7.20.5",
42 | "@types/convert-source-map": "^2.0.3",
43 | "@types/debug": "^4.1.12",
44 | "@types/fs-extra": "^11.0.4",
45 | "@types/node": "^22.15.19",
46 | "@vitejs/release-scripts": "^1.5.0",
47 | "conventional-changelog-cli": "^5.0.0",
48 | "eslint": "^9.27.0",
49 | "eslint-import-resolver-typescript": "^4.3.5",
50 | "eslint-plugin-import-x": "^4.12.2",
51 | "eslint-plugin-n": "^17.18.0",
52 | "eslint-plugin-regexp": "^2.7.0",
53 | "execa": "^9.5.3",
54 | "fs-extra": "^11.3.0",
55 | "lint-staged": "^16.0.0",
56 | "picocolors": "^1.1.1",
57 | "playwright-chromium": "^1.52.0",
58 | "prettier": "3.5.3",
59 | "rollup": "^4.40.2",
60 | "simple-git-hooks": "^2.13.0",
61 | "tsx": "^4.19.4",
62 | "typescript": "^5.8.3",
63 | "typescript-eslint": "^8.32.1",
64 | "unbuild": "3.5.0",
65 | "vite": "catalog:",
66 | "vitest": "^3.1.4",
67 | "vue": "catalog:"
68 | },
69 | "simple-git-hooks": {
70 | "pre-commit": "pnpm exec lint-staged --concurrent false"
71 | },
72 | "lint-staged": {
73 | "*": [
74 | "prettier --write --cache --ignore-unknown"
75 | ],
76 | "packages/*/{src,types}/**/*.ts": [
77 | "eslint --cache --fix"
78 | ],
79 | "packages/**/*.d.ts": [
80 | "eslint --cache --fix"
81 | ],
82 | "playground/**/__tests__/**/*.ts": [
83 | "eslint --cache --fix"
84 | ]
85 | },
86 | "packageManager": "pnpm@10.11.0",
87 | "pnpm": {
88 | "overrides": {
89 | "@vitejs/plugin-vue": "workspace:*"
90 | },
91 | "ignoredBuiltDependencies": [
92 | "@parcel/watcher",
93 | "core-js",
94 | "esbuild"
95 | ],
96 | "onlyBuiltDependencies": [
97 | "playwright-chromium",
98 | "simple-git-hooks"
99 | ]
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/packages/plugin-vue-jsx/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019-present, Yuxi (Evan) You and Vite contributors
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 |
--------------------------------------------------------------------------------
/packages/plugin-vue-jsx/README.md:
--------------------------------------------------------------------------------
1 | # @vitejs/plugin-vue-jsx [](https://npmjs.com/package/@vitejs/plugin-vue-jsx)
2 |
3 | Provides Vue 3 JSX & TSX support with HMR.
4 |
5 | ```js
6 | // vite.config.js
7 | import vueJsx from '@vitejs/plugin-vue-jsx'
8 |
9 | export default {
10 | plugins: [
11 | vueJsx({
12 | // options are passed on to @vue/babel-plugin-jsx
13 | }),
14 | ],
15 | }
16 | ```
17 |
18 | ## Options
19 |
20 | ### include
21 |
22 | Type: `(string | RegExp)[] | string | RegExp | null`
23 |
24 | Default: `/\.[jt]sx$/`
25 |
26 | A [picomatch pattern](https://github.com/micromatch/picomatch), or array of patterns, which specifies the files the plugin should operate on.
27 |
28 | ### exclude
29 |
30 | Type: `(string | RegExp)[] | string | RegExp | null`
31 |
32 | Default: `undefined`
33 |
34 | A [picomatch pattern](https://github.com/micromatch/picomatch), or array of patterns, which specifies the files to be ignored by the plugin.
35 |
36 | > See [@vue/babel-plugin-jsx](https://github.com/vuejs/jsx-next) for other options.
37 |
38 | ### defineComponentName
39 |
40 | Type: `string[]`
41 |
42 | Default: `['defineComponent']`
43 |
44 | The name of the function to be used for defining components. This is useful when you have a custom `defineComponent` function.
45 |
46 | ## HMR Detection
47 |
48 | This plugin supports HMR of Vue JSX components. The detection requirements are:
49 |
50 | - The component must be exported.
51 | - The component must be declared by calling `defineComponent` or the name specified in `defineComponentName` via a root-level statement, either variable declaration or export declaration.
52 |
53 | ### Supported patterns
54 |
55 | ```jsx
56 | import { defineComponent } from 'vue'
57 |
58 | // named exports w/ variable declaration: ok
59 | export const Foo = defineComponent({})
60 |
61 | // named exports referencing variable declaration: ok
62 | const Bar = defineComponent({ render() { return Test
}})
63 | export { Bar }
64 |
65 | // default export call: ok
66 | export default defineComponent({ render() { return Test
}})
67 |
68 | // default export referencing variable declaration: ok
69 | const Baz = defineComponent({ render() { return Test
}})
70 | export default Baz
71 | ```
72 |
73 | ### Non-supported patterns
74 |
75 | ```jsx
76 | // not using `defineComponent` call
77 | export const Bar = { ... }
78 |
79 | // not exported
80 | const Foo = defineComponent(...)
81 | ```
82 |
--------------------------------------------------------------------------------
/packages/plugin-vue-jsx/build.config.ts:
--------------------------------------------------------------------------------
1 | import { defineBuildConfig } from 'unbuild'
2 |
3 | export default defineBuildConfig({
4 | entries: ['src/index'],
5 | clean: true,
6 | declaration: true,
7 | rollup: {
8 | emitCJS: true,
9 | },
10 | })
11 |
--------------------------------------------------------------------------------
/packages/plugin-vue-jsx/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@vitejs/plugin-vue-jsx",
3 | "version": "4.2.0",
4 | "type": "commonjs",
5 | "license": "MIT",
6 | "author": "Evan You",
7 | "files": [
8 | "dist"
9 | ],
10 | "main": "./dist/index.cjs",
11 | "module": "./dist/index.mjs",
12 | "types": "./dist/index.d.ts",
13 | "exports": {
14 | ".": {
15 | "import": "./dist/index.mjs",
16 | "require": "./dist/index.cjs"
17 | }
18 | },
19 | "scripts": {
20 | "dev": "unbuild --stub",
21 | "build": "unbuild && pnpm run patch-cjs",
22 | "patch-cjs": "tsx ../../scripts/patchCJS.ts",
23 | "prepublishOnly": "npm run build"
24 | },
25 | "engines": {
26 | "node": "^18.0.0 || >=20.0.0"
27 | },
28 | "repository": {
29 | "type": "git",
30 | "url": "git+https://github.com/vitejs/vite-plugin-vue.git",
31 | "directory": "packages/plugin-vue-jsx"
32 | },
33 | "bugs": {
34 | "url": "https://github.com/vitejs/vite-plugin-vue/issues"
35 | },
36 | "homepage": "https://github.com/vitejs/vite-plugin-vue/tree/main/packages/plugin-vue-jsx#readme",
37 | "dependencies": {
38 | "@babel/core": "^7.27.1",
39 | "@babel/plugin-transform-typescript": "^7.27.1",
40 | "@rolldown/pluginutils": "^1.0.0-beta.9",
41 | "@vue/babel-plugin-jsx": "^1.4.0"
42 | },
43 | "devDependencies": {
44 | "vite": "catalog:"
45 | },
46 | "peerDependencies": {
47 | "vite": "^5.0.0 || ^6.0.0",
48 | "vue": "^3.0.0"
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/packages/plugin-vue-jsx/src/types.ts:
--------------------------------------------------------------------------------
1 | import type { VueJSXPluginOptions } from '@vue/babel-plugin-jsx'
2 | import type { FilterPattern } from 'vite'
3 |
4 | export interface FilterOptions {
5 | include?: FilterPattern
6 | exclude?: FilterPattern
7 | }
8 |
9 | export interface Options extends VueJSXPluginOptions, FilterOptions {
10 | babelPlugins?: any[]
11 | /** @default ['defineComponent'] */
12 | defineComponentName?: string[]
13 | tsPluginOptions?: any
14 | }
15 |
--------------------------------------------------------------------------------
/packages/plugin-vue-jsx/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "include": ["src"],
3 | "exclude": ["**/*.spec.ts"],
4 | "compilerOptions": {
5 | "outDir": "dist",
6 | "target": "ES2020",
7 | "module": "ES2020",
8 | "moduleResolution": "Node",
9 | "strict": true,
10 | "declaration": true,
11 | "sourceMap": true,
12 | "noUnusedLocals": true,
13 | "esModuleInterop": true
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/packages/plugin-vue/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019-present, Yuxi (Evan) You and Vite contributors
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 |
--------------------------------------------------------------------------------
/packages/plugin-vue/README.md:
--------------------------------------------------------------------------------
1 | # @vitejs/plugin-vue [](https://npmjs.com/package/@vitejs/plugin-vue)
2 |
3 | > Note: as of `vue` 3.2.13+ and `@vitejs/plugin-vue` 1.9.0+, `@vue/compiler-sfc` is no longer required as a peer dependency.
4 |
5 | ```js
6 | // vite.config.js
7 | import vue from '@vitejs/plugin-vue'
8 |
9 | export default {
10 | plugins: [vue()],
11 | }
12 | ```
13 |
14 | For JSX / TSX support, [`@vitejs/plugin-vue-jsx`](https://github.com/vitejs/vite-plugin-vue/tree/main/packages/plugin-vue-jsx) is also needed.
15 |
16 | ## Options
17 |
18 | ```ts
19 | export interface Options {
20 | include?: string | RegExp | (string | RegExp)[]
21 | exclude?: string | RegExp | (string | RegExp)[]
22 |
23 | isProduction?: boolean
24 |
25 | /**
26 | * Requires @vitejs/plugin-vue@^5.1.0
27 | */
28 | features?: {
29 | /**
30 | * Enable reactive destructure for `defineProps`.
31 | * - Available in Vue 3.4 and later.
32 | * - **default:** `false` in Vue 3.4 (**experimental**), `true` in Vue 3.5+
33 | */
34 | propsDestructure?: boolean
35 | /**
36 | * Transform Vue SFCs into custom elements.
37 | * - `true`: all `*.vue` imports are converted into custom elements
38 | * - `string | RegExp`: matched files are converted into custom elements
39 | * - **default:** /\.ce\.vue$/
40 | */
41 | customElement?: boolean | string | RegExp | (string | RegExp)[]
42 | /**
43 | * Set to `false` to disable Options API support and allow related code in
44 | * Vue core to be dropped via dead-code elimination in production builds,
45 | * resulting in smaller bundles.
46 | * - **default:** `true`
47 | */
48 | optionsAPI?: boolean
49 | /**
50 | * Set to `true` to enable devtools support in production builds.
51 | * Results in slightly larger bundles.
52 | * - **default:** `false`
53 | */
54 | prodDevtools?: boolean
55 | /**
56 | * Set to `true` to enable detailed information for hydration mismatch
57 | * errors in production builds. Results in slightly larger bundles.
58 | * - **default:** `false`
59 | */
60 | prodHydrationMismatchDetails?: boolean
61 | /**
62 | * Customize the component ID generation strategy.
63 | * - `'filepath'`: hash the file path (relative to the project root)
64 | * - `'filepath-source'`: hash the file path and the source code
65 | * - `function`: custom function that takes the file path, source code,
66 | * whether in production mode, and the default hash function as arguments
67 | * - **default:** `'filepath'` in development, `'filepath-source'` in production
68 | */
69 | componentIdGenerator?:
70 | | 'filepath'
71 | | 'filepath-source'
72 | | ((
73 | filepath: string,
74 | source: string,
75 | isProduction: boolean | undefined,
76 | getHash: (text: string) => string,
77 | ) => string)
78 | }
79 |
80 | // `script`, `template` and `style` are lower-level compiler options
81 | // to pass on to respective APIs of `vue/compiler-sfc`
82 |
83 | script?: Partial<
84 | Omit<
85 | SFCScriptCompileOptions,
86 | | 'id'
87 | | 'isProd'
88 | | 'inlineTemplate'
89 | | 'templateOptions'
90 | | 'sourceMap'
91 | | 'genDefaultAs'
92 | | 'customElement'
93 | >
94 | >
95 |
96 | template?: Partial<
97 | Omit<
98 | SFCTemplateCompileOptions,
99 | | 'id'
100 | | 'source'
101 | | 'ast'
102 | | 'filename'
103 | | 'scoped'
104 | | 'slotted'
105 | | 'isProd'
106 | | 'inMap'
107 | | 'ssr'
108 | | 'ssrCssVars'
109 | | 'preprocessLang'
110 | >
111 | >
112 |
113 | style?: Partial<
114 | Omit<
115 | SFCStyleCompileOptions,
116 | | 'filename'
117 | | 'id'
118 | | 'isProd'
119 | | 'source'
120 | | 'scoped'
121 | | 'cssDevSourcemap'
122 | | 'postcssOptions'
123 | | 'map'
124 | | 'postcssPlugins'
125 | | 'preprocessCustomRequire'
126 | | 'preprocessLang'
127 | | 'preprocessOptions'
128 | >
129 | >
130 |
131 | /**
132 | * Use custom compiler-sfc instance. Can be used to force a specific version.
133 | */
134 | compiler?: typeof _compiler
135 |
136 | /**
137 | * @deprecated moved to `features.customElement`.
138 | */
139 | customElements?: boolean | string | RegExp | (string | RegExp)[]
140 | }
141 | ```
142 |
143 | ## Asset URL handling
144 |
145 | When `@vitejs/plugin-vue` compiles the `` blocks in SFCs, it also converts any encountered asset URLs into ESM imports.
146 |
147 | For example, the following template snippet:
148 |
149 | ```vue
150 |
151 | ```
152 |
153 | Is the same as:
154 |
155 | ```vue
156 |
159 |
160 |
161 | ```
162 |
163 | By default the following tag/attribute combinations are transformed, and can be configured using the `template.transformAssetUrls` option.
164 |
165 | ```js
166 | {
167 | video: ['src', 'poster'],
168 | source: ['src'],
169 | img: ['src'],
170 | image: ['xlink:href', 'href'],
171 | use: ['xlink:href', 'href']
172 | }
173 | ```
174 |
175 | Note that only attribute values that are static strings are transformed. Otherwise, you'd need to import the asset manually, e.g. `import imgUrl from '../image.png'`.
176 |
177 | ## Example for passing options to `vue/compiler-sfc`:
178 |
179 | ```ts
180 | import vue from '@vitejs/plugin-vue'
181 |
182 | export default {
183 | plugins: [
184 | vue({
185 | template: {
186 | compilerOptions: {
187 | // ...
188 | },
189 | transformAssetUrls: {
190 | // ...
191 | },
192 | },
193 | }),
194 | ],
195 | }
196 | ```
197 |
198 | ## Example for transforming custom blocks
199 |
200 | ```ts
201 | import vue from '@vitejs/plugin-vue'
202 | import yaml from 'js-yaml'
203 |
204 | const vueI18nPlugin = {
205 | name: 'vue-i18n',
206 | transform(code, id) {
207 | // if .vue file don't have block, just return
208 | if (!/vue&type=i18n/.test(id)) {
209 | return
210 | }
211 | // parse yaml
212 | if (/\.ya?ml$/.test(id)) {
213 | code = JSON.stringify(yaml.load(code.trim()))
214 | }
215 | // mount the value on the i18n property of the component instance
216 | return `export default Comp => {
217 | Comp.i18n = ${code}
218 | }`
219 | },
220 | }
221 |
222 | export default {
223 | plugins: [vue(), vueI18nPlugin],
224 | }
225 | ```
226 |
227 | Create a file named `Demo.vue`, add `lang="yaml"` to the `` blocks, then you can use the syntax of `YAML`:
228 |
229 | ```vue
230 | Hello
231 |
232 |
233 | message: 'world'
234 | fullWord: 'hello world'
235 |
236 | ```
237 |
238 | `message` is mounted on the i18n property of the component instance, you can use like this:
239 |
240 | ```vue
241 |
244 |
245 |
246 | {{ Demo.i18n.message }}
247 | {{ Demo.i18n.fullWord }}
248 |
249 | ```
250 |
251 | ## Using Vue SFCs as Custom Elements
252 |
253 | > Requires `vue@^3.2.0` & `@vitejs/plugin-vue@^1.4.0`
254 |
255 | Vue 3.2 introduces the `defineCustomElement` method, which works with SFCs. By default, `
37 |
--------------------------------------------------------------------------------
/playground/ssr-vue/src/assets/button.css:
--------------------------------------------------------------------------------
1 | .btn {
2 | background-color: #65b587;
3 | border-radius: 8px;
4 | border-style: none;
5 | box-sizing: border-box;
6 | cursor: pointer;
7 | display: inline-block;
8 | font-size: 14px;
9 | font-weight: 500;
10 | height: 40px;
11 | line-height: 20px;
12 | list-style: none;
13 | outline: none;
14 | padding: 10px 16px;
15 | }
16 |
--------------------------------------------------------------------------------
/playground/ssr-vue/src/assets/fonts/Inter-Italic.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vitejs/vite-plugin-vue/5f43900edd3d73d10a1655be54a33ac46b267fbd/playground/ssr-vue/src/assets/fonts/Inter-Italic.woff
--------------------------------------------------------------------------------
/playground/ssr-vue/src/assets/fonts/Inter-Italic.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vitejs/vite-plugin-vue/5f43900edd3d73d10a1655be54a33ac46b267fbd/playground/ssr-vue/src/assets/fonts/Inter-Italic.woff2
--------------------------------------------------------------------------------
/playground/ssr-vue/src/assets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vitejs/vite-plugin-vue/5f43900edd3d73d10a1655be54a33ac46b267fbd/playground/ssr-vue/src/assets/logo.png
--------------------------------------------------------------------------------
/playground/ssr-vue/src/components/Foo.jsx:
--------------------------------------------------------------------------------
1 | import { defineComponent } from 'vue'
2 | import './foo.css'
3 |
4 | // named exports w/ variable declaration: ok
5 | export const Foo = defineComponent({
6 | name: 'foo',
7 | setup() {
8 | return () => from JSX
9 | },
10 | })
11 |
--------------------------------------------------------------------------------
/playground/ssr-vue/src/components/ImportType.vue:
--------------------------------------------------------------------------------
1 |
2 | import type should be removed without side-effect
3 |
4 |
5 |
12 |
--------------------------------------------------------------------------------
/playground/ssr-vue/src/components/button.js:
--------------------------------------------------------------------------------
1 | import { createVNode, defineComponent } from 'vue'
2 | import '../assets/button.css'
3 |
4 | export default defineComponent({
5 | setup() {
6 | return () => {
7 | return createVNode(
8 | 'div',
9 | {
10 | class: 'btn',
11 | },
12 | 'dynamicBtn',
13 | )
14 | }
15 | },
16 | })
17 |
--------------------------------------------------------------------------------
/playground/ssr-vue/src/components/foo.css:
--------------------------------------------------------------------------------
1 | .jsx {
2 | color: blue;
3 | }
4 |
--------------------------------------------------------------------------------
/playground/ssr-vue/src/entry-client.js:
--------------------------------------------------------------------------------
1 | import { createApp } from './main'
2 |
3 | const { app, router } = createApp()
4 |
5 | // wait until router is ready before mounting to ensure hydration match
6 | router.isReady().then(() => {
7 | app.mount('#app')
8 |
9 | console.log('hydrated')
10 | })
11 |
--------------------------------------------------------------------------------
/playground/ssr-vue/src/entry-server.js:
--------------------------------------------------------------------------------
1 | import { basename } from 'node:path'
2 | import { renderToString } from 'vue/server-renderer'
3 | import { createApp } from './main'
4 |
5 | export async function render(url, manifest) {
6 | const { app, router } = createApp()
7 |
8 | // set the router to the desired URL before rendering
9 | await router.push(url)
10 | await router.isReady()
11 |
12 | // passing SSR context object which will be available via useSSRContext()
13 | // @vitejs/plugin-vue injects code into a component's setup() that registers
14 | // itself on ctx.modules. After the render, ctx.modules would contain all the
15 | // components that have been instantiated during this render call.
16 | const ctx = {}
17 | const html = await renderToString(app, ctx)
18 |
19 | // the SSR manifest generated by Vite contains module -> chunk/asset mapping
20 | // which we can then use to determine what files need to be preloaded for this
21 | // request.
22 | const preloadLinks = renderPreloadLinks(ctx.modules, manifest)
23 | return [html, preloadLinks]
24 | }
25 |
26 | function renderPreloadLinks(modules, manifest) {
27 | let links = ''
28 | const seen = new Set()
29 | modules.forEach((id) => {
30 | const files = manifest[id]
31 | if (files) {
32 | files.forEach((file) => {
33 | if (!seen.has(file)) {
34 | seen.add(file)
35 | const filename = basename(file)
36 | if (manifest[filename]) {
37 | for (const depFile of manifest[filename]) {
38 | links += renderPreloadLink(depFile)
39 | seen.add(depFile)
40 | }
41 | }
42 | links += renderPreloadLink(file)
43 | }
44 | })
45 | }
46 | })
47 | return links
48 | }
49 |
50 | function renderPreloadLink(file) {
51 | if (file.endsWith('.js')) {
52 | return ``
53 | } else if (file.endsWith('.css')) {
54 | return ``
55 | } else if (file.endsWith('.woff')) {
56 | return ` `
57 | } else if (file.endsWith('.woff2')) {
58 | return ` `
59 | } else if (file.endsWith('.gif')) {
60 | return ` `
61 | } else if (file.endsWith('.jpg') || file.endsWith('.jpeg')) {
62 | return ` `
63 | } else if (file.endsWith('.png')) {
64 | return ` `
65 | } else {
66 | // TODO
67 | return ''
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/playground/ssr-vue/src/main.js:
--------------------------------------------------------------------------------
1 | import { createPinia } from 'pinia'
2 | import { createSSRApp } from 'vue'
3 | import App from './App.vue'
4 | import { createRouter } from './router'
5 |
6 | // SSR requires a fresh app instance per request, therefore we export a function
7 | // that creates a fresh app instance. If using Vuex, we'd also be creating a
8 | // fresh store here.
9 | export function createApp() {
10 | const app = createSSRApp(App)
11 | const pinia = createPinia()
12 | app.use(pinia)
13 | const router = createRouter()
14 | app.use(router)
15 | return { app, router }
16 | }
17 |
--------------------------------------------------------------------------------
/playground/ssr-vue/src/pages/About.vue:
--------------------------------------------------------------------------------
1 |
2 | {{ msg }}
3 | {{ url }}
4 |
5 |
6 |
7 |
25 |
26 |
31 |
--------------------------------------------------------------------------------
/playground/ssr-vue/src/pages/External.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
14 |
--------------------------------------------------------------------------------
/playground/ssr-vue/src/pages/Home.vue:
--------------------------------------------------------------------------------
1 |
2 | Home
3 |
4 |
5 |
6 |
7 |
8 | msg from virtual module: {{ foo.msg }}
9 | this will be styled with a font-face
10 | {{ state.url }}
11 | {{ state.protocol }}
12 | msg from nested virtual module: {{ virtualMsg }}
13 |
14 |
15 | encrypted message:
16 |
17 |
18 |
19 |
20 |
21 |
22 |
45 |
46 |
52 |
--------------------------------------------------------------------------------
/playground/ssr-vue/src/pages/Store.vue:
--------------------------------------------------------------------------------
1 |
2 | {{ fooStore.foo }}
3 |
4 |
5 |
20 |
21 |
26 |
--------------------------------------------------------------------------------
/playground/ssr-vue/src/router.js:
--------------------------------------------------------------------------------
1 | import {
2 | createRouter as _createRouter,
3 | createMemoryHistory,
4 | createWebHistory,
5 | } from 'vue-router'
6 |
7 | // Auto generates routes from vue files under ./pages
8 | // https://vitejs.dev/guide/features.html#glob-import
9 | const pages = import.meta.glob('./pages/*.vue')
10 |
11 | const routes = Object.keys(pages).map((path) => {
12 | const name = path.match(/\.\/pages(.*)\.vue$/)[1].toLowerCase()
13 | return {
14 | path: name === '/home' ? '/' : name,
15 | component: pages[path], // () => import('./pages/*.vue')
16 | }
17 | })
18 |
19 | export function createRouter() {
20 | return _createRouter({
21 | // use appropriate history implementation for server/client
22 | // import.meta.env.SSR is injected by Vite.
23 | history: import.meta.env.SSR
24 | ? createMemoryHistory('/test/')
25 | : createWebHistory('/test/'),
26 | routes,
27 | })
28 | }
29 |
--------------------------------------------------------------------------------
/playground/ssr-vue/vite.config.js:
--------------------------------------------------------------------------------
1 | import path from 'node:path'
2 | import { defineConfig } from 'vite'
3 | import vuePlugin from '@vitejs/plugin-vue'
4 | import vueJsx from '@vitejs/plugin-vue-jsx'
5 |
6 | const virtualFile = '@virtual-file'
7 | const virtualId = '\0' + virtualFile
8 | const nestedVirtualFile = '@nested-virtual-file'
9 | const nestedVirtualId = '\0' + nestedVirtualFile
10 |
11 | const base = '/test/'
12 |
13 | // preserve this to test loading __filename & __dirname in ESM as Vite polyfills them.
14 | // if Vite incorrectly load this file, node.js would error out.
15 | globalThis.__vite_test_filename = __filename
16 | globalThis.__vite_test_dirname = __dirname
17 |
18 | export default defineConfig(({ command, ssrBuild, isSsrBuild }) => ({
19 | base,
20 | plugins: [
21 | vuePlugin(),
22 | vueJsx(),
23 | {
24 | name: 'virtual',
25 | resolveId(id) {
26 | if (id === '@foo') {
27 | return id
28 | }
29 | },
30 | load(id, options) {
31 | const ssrFromOptions = options?.ssr ?? false
32 | if (id === '@foo') {
33 | // Force a mismatch error if ssrBuild/isSsrBuild is different from ssrFromOptions
34 | return `export default { msg: '${
35 | command === 'build' && !!(ssrBuild ?? isSsrBuild) !== ssrFromOptions
36 | ? `defineConfig ssrBuild !== ssr from load options`
37 | : 'hi'
38 | }' }`
39 | }
40 | },
41 | },
42 | {
43 | name: 'virtual-module',
44 | resolveId(id) {
45 | if (id === virtualFile) {
46 | return virtualId
47 | } else if (id === nestedVirtualFile) {
48 | return nestedVirtualId
49 | }
50 | },
51 | load(id) {
52 | if (id === virtualId) {
53 | return `export { msg } from "@nested-virtual-file";`
54 | } else if (id === nestedVirtualId) {
55 | return `export const msg = "[success] from conventional virtual file"`
56 | }
57 | },
58 | },
59 | // Example of a plugin that injects a helper from a virtual module that can
60 | // be used in renderBuiltUrl
61 | (function () {
62 | const queryRE = /\?.*$/s
63 | const hashRE = /#.*$/s
64 | const cleanUrl = (url) => url.replace(hashRE, '').replace(queryRE, '')
65 | let config
66 |
67 | const virtualId = '\0virtual:ssr-vue-built-url'
68 | return {
69 | name: 'built-url',
70 | enforce: 'post',
71 | configResolved(_config) {
72 | config = _config
73 | },
74 | resolveId(id) {
75 | if (id === virtualId) {
76 | return id
77 | }
78 | },
79 | load(id) {
80 | if (id === virtualId) {
81 | return {
82 | code: `export const __ssr_vue_processAssetPath = (url) => '${base}' + url`,
83 | moduleSideEffects: 'no-treeshake',
84 | }
85 | }
86 | },
87 | transform(code, id) {
88 | const cleanId = cleanUrl(id)
89 | if (
90 | config.build.ssr &&
91 | (cleanId.endsWith('.js') || cleanId.endsWith('.vue')) &&
92 | !code.includes('__ssr_vue_processAssetPath')
93 | ) {
94 | return {
95 | code:
96 | `import { __ssr_vue_processAssetPath } from '${virtualId}';` +
97 | // make `__ssr_vue_processAssetPath` not to be tree-shaken (`globalThis.__ssr_vue` doesn't exist)
98 | `globalThis.__ssr_vue?.(__ssr_vue_processAssetPath);` +
99 | code,
100 | sourcemap: null, // no sourcemap support to speed up CI
101 | }
102 | }
103 | },
104 | }
105 | })(),
106 | ],
107 | experimental: {
108 | renderBuiltUrl(filename, { hostType, type, ssr }) {
109 | if (ssr && type === 'asset' && hostType === 'js') {
110 | return {
111 | runtime: `__ssr_vue_processAssetPath(${JSON.stringify(filename)})`,
112 | }
113 | }
114 | },
115 | },
116 | build: {
117 | minify: false,
118 | },
119 | ssr: {
120 | noExternal: [
121 | // this package has uncompiled .vue files
122 | '@vitejs/test-example-external-component',
123 | ],
124 | },
125 | optimizeDeps: {
126 | exclude: ['@vitejs/test-example-external-component'],
127 | },
128 | }))
129 |
--------------------------------------------------------------------------------
/playground/ssr-vue/vite.config.noexternal.js:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite'
2 | import createConfig from './vite.config.js'
3 |
4 | export default defineConfig((env) => {
5 | const config = createConfig(env)
6 | return Object.assign(config, {
7 | ssr: {
8 | noExternal: /./,
9 | },
10 | resolve: {
11 | // necessary because vue.ssrUtils is only exported on cjs modules
12 | alias: [
13 | {
14 | find: '@vue/runtime-dom',
15 | replacement: '@vue/runtime-dom/dist/runtime-dom.cjs.js',
16 | },
17 | {
18 | find: '@vue/runtime-core',
19 | replacement: '@vue/runtime-core/dist/runtime-core.cjs.js',
20 | },
21 | ],
22 | },
23 | optimizeDeps: {
24 | exclude: ['@vitejs/test-example-external-component'],
25 | },
26 | })
27 | })
28 |
--------------------------------------------------------------------------------
/playground/tailwind-v3/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
8 |
--------------------------------------------------------------------------------
/playground/tailwind-v3/PugTemplate.vue:
--------------------------------------------------------------------------------
1 |
2 | .bg-red-400.pug Pug template
3 |
4 |
--------------------------------------------------------------------------------
/playground/tailwind-v3/__tests__/tailwind.spec.ts:
--------------------------------------------------------------------------------
1 | import { expect, test } from 'vitest'
2 | import {
3 | editFile,
4 | getBgColor,
5 | isServe,
6 | page,
7 | untilBrowserLogAfter,
8 | untilUpdated,
9 | } from '~utils'
10 |
11 | test.runIf(isServe)('regenerate CSS and HMR (pug template)', async () => {
12 | const el = await page.$('.pug')
13 | expect(await getBgColor(el)).toBe('rgb(248, 113, 113)')
14 |
15 | await untilBrowserLogAfter(
16 | () =>
17 | editFile('PugTemplate.vue', (code) =>
18 | code.replace('bg-red-400', 'bg-red-600'),
19 | ),
20 | [
21 | '[vite] css hot updated: /index.css',
22 | '[vite] hot updated: /PugTemplate.vue?vue&type=template&lang.js',
23 | ],
24 | false,
25 | )
26 | await untilUpdated(() => getBgColor(el), 'rgb(220, 38, 38)')
27 | })
28 |
--------------------------------------------------------------------------------
/playground/tailwind-v3/index.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
--------------------------------------------------------------------------------
/playground/tailwind-v3/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
--------------------------------------------------------------------------------
/playground/tailwind-v3/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@vitejs/test-tailwind-v3",
3 | "private": true,
4 | "version": "0.0.0",
5 | "type": "module",
6 | "scripts": {
7 | "dev": "vite",
8 | "build": "vite build",
9 | "debug": "node --inspect-brk ../../packages/vite/bin/vite",
10 | "preview": "vite preview"
11 | },
12 | "dependencies": {
13 | "autoprefixer": "^10.4.21",
14 | "tailwindcss": "^3.4.17",
15 | "vue": "catalog:"
16 | },
17 | "devDependencies": {
18 | "@types/node": "^22.15.19",
19 | "@vitejs/plugin-vue": "workspace:*",
20 | "ts-node": "^10.9.2"
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/playground/tailwind-v3/postcss.config.cts:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: {
3 | tailwindcss: { config: __dirname + '/tailwind.config.js' },
4 | autoprefixer: {},
5 | },
6 | }
7 |
--------------------------------------------------------------------------------
/playground/tailwind-v3/tailwind.config.js:
--------------------------------------------------------------------------------
1 | import { fileURLToPath } from 'node:url'
2 | import { dirname } from 'node:path'
3 |
4 | const __filename = fileURLToPath(import.meta.url)
5 | const __dirname = dirname(__filename)
6 |
7 | /** @type {import('tailwindcss').Config} */
8 | export default {
9 | content: [__dirname + '/**/*.vue'],
10 | theme: {
11 | extend: {},
12 | },
13 | variants: {
14 | extend: {},
15 | },
16 | plugins: [],
17 | }
18 |
--------------------------------------------------------------------------------
/playground/tailwind-v3/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite'
2 | import vue from '@vitejs/plugin-vue'
3 |
4 | export default defineConfig({
5 | plugins: [vue()],
6 | build: {
7 | // to make tests faster
8 | minify: false,
9 | },
10 | })
11 |
--------------------------------------------------------------------------------
/playground/tailwind/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
8 |
--------------------------------------------------------------------------------
/playground/tailwind/PugTemplate.vue:
--------------------------------------------------------------------------------
1 |
2 | .bg-red-400.pug Pug template
3 |
4 |
--------------------------------------------------------------------------------
/playground/tailwind/__tests__/tailwind.spec.ts:
--------------------------------------------------------------------------------
1 | import { expect, test } from 'vitest'
2 | import {
3 | editFile,
4 | getBgColor,
5 | isServe,
6 | page,
7 | untilBrowserLogAfter,
8 | untilUpdated,
9 | } from '~utils'
10 |
11 | test.runIf(isServe)('regenerate CSS and HMR (pug template)', async () => {
12 | const el = await page.$('.pug')
13 | expect(await getBgColor(el)).toBe('oklch(0.704 0.191 22.216)')
14 |
15 | await untilBrowserLogAfter(
16 | () =>
17 | editFile('PugTemplate.vue', (code) =>
18 | code.replace('bg-red-400', 'bg-red-600'),
19 | ),
20 | [
21 | '[vite] css hot updated: /index.css',
22 | '[vite] hot updated: /PugTemplate.vue?vue&type=template&lang.js',
23 | ],
24 | false,
25 | )
26 | await untilUpdated(() => getBgColor(el), 'oklch(0.577 0.245 27.325)')
27 | })
28 |
--------------------------------------------------------------------------------
/playground/tailwind/index.css:
--------------------------------------------------------------------------------
1 | @import 'tailwindcss';
2 |
--------------------------------------------------------------------------------
/playground/tailwind/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
--------------------------------------------------------------------------------
/playground/tailwind/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@vitejs/test-tailwind",
3 | "private": true,
4 | "version": "0.0.0",
5 | "type": "module",
6 | "scripts": {
7 | "dev": "vite",
8 | "build": "vite build",
9 | "debug": "node --inspect-brk ../../packages/vite/bin/vite",
10 | "preview": "vite preview"
11 | },
12 | "dependencies": {
13 | "@tailwindcss/vite": "^4.1.7",
14 | "tailwindcss": "^4.1.7",
15 | "vue": "catalog:"
16 | },
17 | "devDependencies": {
18 | "@types/node": "^22.15.19",
19 | "@vitejs/plugin-vue": "workspace:*"
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/playground/tailwind/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite'
2 | import vue from '@vitejs/plugin-vue'
3 | import tailwind from '@tailwindcss/vite'
4 |
5 | export default defineConfig({
6 | plugins: [vue(), tailwind()],
7 | build: {
8 | // to make tests faster
9 | minify: false,
10 | },
11 | })
12 |
--------------------------------------------------------------------------------
/playground/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "include": ["."],
3 | "exclude": ["**/dist/**"],
4 | "compilerOptions": {
5 | "target": "ES2020",
6 | "module": "ESNext",
7 | "outDir": "dist",
8 | "baseUrl": ".",
9 | "allowJs": true,
10 | "esModuleInterop": true,
11 | "resolveJsonModule": true,
12 | "moduleResolution": "bundler",
13 | "skipLibCheck": true,
14 | "noUnusedLocals": true,
15 | "jsx": "preserve",
16 | "types": ["vite/client", "node"],
17 | "paths": {
18 | "~utils": ["./test-utils.ts"]
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/playground/vitestGlobalSetup.ts:
--------------------------------------------------------------------------------
1 | import os from 'node:os'
2 | import path from 'node:path'
3 | import fs from 'fs-extra'
4 | import type { BrowserServer } from 'playwright-chromium'
5 | import { chromium } from 'playwright-chromium'
6 |
7 | const DIR = path.join(os.tmpdir(), 'vitest_playwright_global_setup')
8 |
9 | let browserServer: BrowserServer | undefined
10 |
11 | export async function setup(): Promise {
12 | browserServer = await chromium.launchServer({
13 | headless: !process.env.VITE_DEBUG_SERVE,
14 | args: process.env.CI
15 | ? ['--no-sandbox', '--disable-setuid-sandbox']
16 | : undefined,
17 | })
18 |
19 | await fs.mkdirp(DIR)
20 | await fs.writeFile(path.join(DIR, 'wsEndpoint'), browserServer.wsEndpoint())
21 |
22 | const tempDir = path.resolve(__dirname, '../playground-temp')
23 | await fs.ensureDir(tempDir)
24 | await fs.emptyDir(tempDir)
25 | await fs
26 | .copy(path.resolve(__dirname, '../playground'), tempDir, {
27 | dereference: false,
28 | filter(file) {
29 | 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 | await browserServer?.close()
46 | if (!process.env.VITE_PRESERVE_BUILD_ARTIFACTS) {
47 | fs.removeSync(path.resolve(__dirname, '../playground-temp'))
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/playground/vue-asset-base/Main.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/playground/vue-asset-base/__tests__/vue-asset-base.spec.ts:
--------------------------------------------------------------------------------
1 | import { expect, test } from 'vitest'
2 | import { isBuild, page } from '~utils'
3 |
4 | test('should render', async () => {
5 | const expected = isBuild
6 | ? /^\/foo\/assets\/asset-[-\w]+\.png/
7 | : /^\/foo\/assets\/asset.png/
8 |
9 | expect(await page.getAttribute('img', 'src')).toMatch(expected)
10 | expect(await page.getAttribute('img:nth-child(2)', 'src')).toMatch(expected)
11 | })
12 |
--------------------------------------------------------------------------------
/playground/vue-asset-base/assets/asset.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vitejs/vite-plugin-vue/5f43900edd3d73d10a1655be54a33ac46b267fbd/playground/vue-asset-base/assets/asset.png
--------------------------------------------------------------------------------
/playground/vue-asset-base/env.d.ts:
--------------------------------------------------------------------------------
1 | declare module '*.png'
2 |
--------------------------------------------------------------------------------
/playground/vue-asset-base/index.html:
--------------------------------------------------------------------------------
1 |
2 |
8 |
--------------------------------------------------------------------------------
/playground/vue-asset-base/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@vitejs/test-vue-server-origin",
3 | "private": true,
4 | "version": "0.0.0",
5 | "type": "module",
6 | "scripts": {
7 | "dev": "vite",
8 | "build": "vite build",
9 | "debug": "node --inspect-brk vite",
10 | "preview": "vite preview"
11 | },
12 | "dependencies": {
13 | "vue": "catalog:"
14 | },
15 | "devDependencies": {
16 | "@vitejs/plugin-vue": "workspace:*"
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/playground/vue-asset-base/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite'
2 | import vuePlugin from '@vitejs/plugin-vue'
3 |
4 | export default defineConfig({
5 | base: '/foo/',
6 | resolve: {
7 | alias: {
8 | '@': __dirname,
9 | },
10 | },
11 | plugins: [vuePlugin()],
12 | build: {
13 | // to make tests faster
14 | minify: false,
15 | },
16 | })
17 |
--------------------------------------------------------------------------------
/playground/vue-custom-id/Main.vue:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/playground/vue-custom-id/__tests__/vue-custom-id.spec.ts:
--------------------------------------------------------------------------------
1 | import { expect, test } from 'vitest'
2 | import { page } from '~utils'
3 |
4 | test('should render', async () => {
5 | expect(await page.innerHTML('div')).toMatch(
6 | 'Foo
',
7 | )
8 | })
9 |
--------------------------------------------------------------------------------
/playground/vue-custom-id/components/Foo.vue:
--------------------------------------------------------------------------------
1 |
2 | Foo
3 |
4 |
5 |
10 |
--------------------------------------------------------------------------------
/playground/vue-custom-id/index.html:
--------------------------------------------------------------------------------
1 |
2 |
8 |
--------------------------------------------------------------------------------
/playground/vue-custom-id/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@vitejs/test-vue-custom-id",
3 | "private": true,
4 | "version": "0.0.0",
5 | "scripts": {
6 | "dev": "vite",
7 | "build": "vite build",
8 | "debug": "node --inspect-brk vite",
9 | "preview": "vite preview"
10 | },
11 | "dependencies": {
12 | "vue": "catalog:"
13 | },
14 | "devDependencies": {
15 | "@vitejs/plugin-vue": "workspace:*"
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/playground/vue-custom-id/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite'
2 | import vuePlugin from '@vitejs/plugin-vue'
3 |
4 | export default defineConfig({
5 | plugins: [
6 | vuePlugin({
7 | features: {
8 | componentIdGenerator: (filename) => {
9 | return filename
10 | .replace(/\.\w+$/, '')
11 | .replace(/[^a-z0-9]/gi, '-')
12 | .toLowerCase()
13 | },
14 | },
15 | }),
16 | ],
17 | build: {
18 | // to make tests faster
19 | minify: false,
20 | },
21 | })
22 |
--------------------------------------------------------------------------------
/playground/vue-external/src-import/SrcImport.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/playground/vue-external/src-import/css.module.css:
--------------------------------------------------------------------------------
1 | .one {
2 | background: yellow;
3 | }
4 |
5 | .two {
6 | border: solid 1px red;
7 | }
8 |
--------------------------------------------------------------------------------
/playground/vue-external/src-import/script.ts:
--------------------------------------------------------------------------------
1 | import { defineComponent } from 'vue'
2 | import SrcImportStyle from './srcImportStyle.vue'
3 | import SrcImportStyle2 from './srcImportStyle2.vue'
4 | import SrcImportModuleStyle from './srcImportModuleStyle.vue'
5 | import SrcImportModuleStyle2 from './srcImportModuleStyle2.vue'
6 |
7 | export default defineComponent({
8 | components: {
9 | SrcImportStyle,
10 | SrcImportStyle2,
11 | SrcImportModuleStyle,
12 | SrcImportModuleStyle2,
13 | },
14 | setup() {
15 | return {
16 | msg: 'hello from script src!',
17 | }
18 | },
19 | })
20 |
--------------------------------------------------------------------------------
/playground/vue-external/src-import/srcImportModuleStyle.vue:
--------------------------------------------------------------------------------
1 |
2 | src for import css module
3 |
4 |
5 |
--------------------------------------------------------------------------------
/playground/vue-external/src-import/srcImportModuleStyle2.vue:
--------------------------------------------------------------------------------
1 |
2 | src for import css module
3 |
4 |
5 |
--------------------------------------------------------------------------------
/playground/vue-external/src-import/srcImportStyle.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | {{ msg }}
4 |
5 |
8 |
--------------------------------------------------------------------------------
/playground/vue-external/src-import/srcImportStyle2.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | This should be tan
4 |
5 |
--------------------------------------------------------------------------------
/playground/vue-external/src-import/style.css:
--------------------------------------------------------------------------------
1 | .external-src-imports-style {
2 | color: tan;
3 | }
4 |
--------------------------------------------------------------------------------
/playground/vue-external/src-import/style2.css:
--------------------------------------------------------------------------------
1 | .external-src-imports-script {
2 | color: #0088ff;
3 | }
4 |
--------------------------------------------------------------------------------
/playground/vue-external/src-import/template.html:
--------------------------------------------------------------------------------
1 | External SFC Src Imports
2 | {{ msg }}
3 | This should be tan
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/playground/vue-jsx/Comp.tsx:
--------------------------------------------------------------------------------
1 | import { defineComponent, ref } from 'vue'
2 |
3 | const Default = defineComponent(() => {
4 | const count = ref(3)
5 | const inc = () => count.value++
6 |
7 | return () => (
8 |
11 | )
12 | })
13 |
14 | export default Default
15 |
--------------------------------------------------------------------------------
/playground/vue-jsx/Comps.jsx:
--------------------------------------------------------------------------------
1 | import { defineComponent, ref } from 'vue'
2 |
3 | export const Named = defineComponent(() => {
4 | const count = ref(0)
5 | const inc = () => count.value++
6 |
7 | return () => (
8 |
11 | )
12 | })
13 |
14 | const NamedSpec = defineComponent(() => {
15 | const count = ref(1)
16 | const inc = () => count.value++
17 |
18 | return () => (
19 |
22 | )
23 | })
24 | export { NamedSpec }
25 |
26 | export default defineComponent(() => {
27 | const count = ref(2)
28 | const inc = () => count.value++
29 |
30 | return () => (
31 |
34 | )
35 | })
36 |
--------------------------------------------------------------------------------
/playground/vue-jsx/OtherExt.tesx:
--------------------------------------------------------------------------------
1 | import { defineComponent } from 'vue'
2 |
3 | const Default = defineComponent(() => {
4 | return () => (
5 | Other Ext
6 | )
7 | })
8 |
9 | export default Default
10 |
--------------------------------------------------------------------------------
/playground/vue-jsx/Query.jsx:
--------------------------------------------------------------------------------
1 | import { defineComponent, ref } from 'vue'
2 |
3 | export default defineComponent(() => {
4 | const count = ref(6)
5 | const inc = () => count.value++
6 |
7 | return () => (
8 |
11 | )
12 | })
13 |
--------------------------------------------------------------------------------
/playground/vue-jsx/Script.vue:
--------------------------------------------------------------------------------
1 |
15 |
--------------------------------------------------------------------------------
/playground/vue-jsx/SrcImport.jsx:
--------------------------------------------------------------------------------
1 | import { defineComponent, ref } from 'vue'
2 |
3 | export default defineComponent(() => {
4 | const count = ref(5)
5 | const inc = () => count.value++
6 |
7 | return () => (
8 |
11 | )
12 | })
13 |
--------------------------------------------------------------------------------
/playground/vue-jsx/SrcImport.vue:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/playground/vue-jsx/TsImport.vue:
--------------------------------------------------------------------------------
1 |
2 | Ts Import
3 | {{ foo }}
4 |
5 |
6 |
7 |
8 |
17 |
--------------------------------------------------------------------------------
/playground/vue-jsx/TsImportFile.ts:
--------------------------------------------------------------------------------
1 | export const foo = 'success'
2 |
--------------------------------------------------------------------------------
/playground/vue-jsx/__tests__/vue-jsx.spec.ts:
--------------------------------------------------------------------------------
1 | import { describe, expect, test } from 'vitest'
2 | import { editFile, isServe, page, untilUpdated } from '~utils'
3 |
4 | test('should render', async () => {
5 | expect(await page.textContent('.named')).toMatch('0')
6 | expect(await page.textContent('.named-specifier')).toMatch('1')
7 | expect(await page.textContent('.default')).toMatch('2')
8 | expect(await page.textContent('.default-tsx')).toMatch('3')
9 | expect(await page.textContent('.script')).toMatch('4')
10 | expect(await page.textContent('.src-import')).toMatch('5')
11 | expect(await page.textContent('.jsx-with-query')).toMatch('6')
12 | expect(await page.textContent('.other-ext')).toMatch('Other Ext')
13 | expect(await page.textContent('.ts-import')).toMatch('success')
14 | })
15 |
16 | test('should update', async () => {
17 | await page.click('.named')
18 | expect(await page.textContent('.named')).toMatch('1')
19 | await page.click('.named-specifier')
20 | expect(await page.textContent('.named-specifier')).toMatch('2')
21 | await page.click('.default')
22 | expect(await page.textContent('.default')).toMatch('3')
23 | await page.click('.default-tsx')
24 | expect(await page.textContent('.default-tsx')).toMatch('4')
25 | await page.click('.script')
26 | expect(await page.textContent('.script')).toMatch('5')
27 | await page.click('.src-import')
28 | expect(await page.textContent('.src-import')).toMatch('6')
29 | await page.click('.jsx-with-query')
30 | expect(await page.textContent('.jsx-with-query')).toMatch('7')
31 | })
32 |
33 | describe.runIf(isServe)('vue-jsx server', () => {
34 | test('hmr: named export', async () => {
35 | editFile('Comps.jsx', (code) =>
36 | code.replace('named {count', 'named updated {count'),
37 | )
38 | await untilUpdated(() => page.textContent('.named'), 'named updated 0')
39 |
40 | // affect all components in same file
41 | expect(await page.textContent('.named-specifier')).toMatch('1')
42 | expect(await page.textContent('.default')).toMatch('2')
43 | // should not affect other components from different file
44 | expect(await page.textContent('.default-tsx')).toMatch('4')
45 | })
46 |
47 | test('hmr: named export via specifier', async () => {
48 | editFile('Comps.jsx', (code) =>
49 | code.replace('named specifier {count', 'named specifier updated {count'),
50 | )
51 | await untilUpdated(
52 | () => page.textContent('.named-specifier'),
53 | 'named specifier updated 1',
54 | )
55 |
56 | // affect all components in same file
57 | expect(await page.textContent('.default')).toMatch('2')
58 | // should not affect other components on the page
59 | expect(await page.textContent('.default-tsx')).toMatch('4')
60 | })
61 |
62 | test('hmr: default export', async () => {
63 | editFile('Comps.jsx', (code) =>
64 | code.replace('default {count', 'default updated {count'),
65 | )
66 | await untilUpdated(() => page.textContent('.default'), 'default updated 2')
67 |
68 | // should not affect other components on the page
69 | expect(await page.textContent('.default-tsx')).toMatch('4')
70 | })
71 |
72 | test('hmr: named export via specifier', async () => {
73 | // update another component
74 | await page.click('.named')
75 | expect(await page.textContent('.named')).toMatch('1')
76 |
77 | editFile('Comp.tsx', (code) =>
78 | code.replace('default tsx {count', 'default tsx updated {count'),
79 | )
80 | await untilUpdated(
81 | () => page.textContent('.default-tsx'),
82 | 'default tsx updated 3',
83 | )
84 |
85 | // should not affect other components on the page
86 | expect(await page.textContent('.named')).toMatch('1')
87 | })
88 |
89 | test('hmr: script in .vue', async () => {
90 | editFile('Script.vue', (code) =>
91 | code.replace('script {count', 'script updated {count'),
92 | )
93 | await untilUpdated(() => page.textContent('.script'), 'script updated 4')
94 |
95 | expect(await page.textContent('.src-import')).toMatch('6')
96 | })
97 |
98 | test('hmr: src import in .vue', async () => {
99 | await page.click('.script')
100 | editFile('SrcImport.jsx', (code) =>
101 | code.replace('src import {count', 'src import updated {count'),
102 | )
103 | await untilUpdated(
104 | () => page.textContent('.src-import'),
105 | 'src import updated 5',
106 | )
107 |
108 | expect(await page.textContent('.script')).toMatch('5')
109 | })
110 |
111 | test('hmr: setup jsx in .vue', async () => {
112 | editFile('setup-syntax-jsx.vue', (code) =>
113 | code.replace('let count = ref(100)', 'let count = ref(1000)'),
114 | )
115 | await untilUpdated(() => page.textContent('.setup-jsx'), '1000')
116 | })
117 | })
118 |
--------------------------------------------------------------------------------
/playground/vue-jsx/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/playground/vue-jsx/main.jsx:
--------------------------------------------------------------------------------
1 | import { createApp } from 'vue'
2 | import { Named, NamedSpec, default as Default } from './Comps'
3 | import { default as TsxDefault } from './Comp'
4 | import OtherExt from './OtherExt.tesx'
5 | import JsxScript from './Script.vue'
6 | import JsxSrcImport from './SrcImport.vue'
7 | import JsxSetupSyntax from './setup-syntax-jsx.vue'
8 | // eslint-disable-next-line
9 | import JsxWithQuery from './Query.jsx?query=true'
10 | import TsImport from './TsImport.vue'
11 |
12 | function App() {
13 | return (
14 | <>
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 | >
26 | )
27 | }
28 |
29 | createApp(App).mount('#app')
30 |
--------------------------------------------------------------------------------
/playground/vue-jsx/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@vitejs/test-vue-jsx",
3 | "private": true,
4 | "version": "0.0.0",
5 | "type": "module",
6 | "scripts": {
7 | "dev": "vite",
8 | "build": "vite build",
9 | "debug": "node --inspect-brk vite",
10 | "preview": "vite preview"
11 | },
12 | "dependencies": {
13 | "vue": "catalog:"
14 | },
15 | "devDependencies": {
16 | "@vitejs/plugin-vue": "workspace:*",
17 | "@vitejs/plugin-vue-jsx": "workspace:*"
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/playground/vue-jsx/setup-syntax-jsx.vue:
--------------------------------------------------------------------------------
1 |
13 |
14 |
15 | {{ count }}
16 |
17 |
18 |
--------------------------------------------------------------------------------
/playground/vue-jsx/vite.config.js:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite'
2 | import vueJsxPlugin from '@vitejs/plugin-vue-jsx'
3 | import vuePlugin from '@vitejs/plugin-vue'
4 |
5 | export default defineConfig({
6 | plugins: [
7 | vueJsxPlugin({
8 | include: [/\.tesx$/, /\.[jt]sx$/],
9 | }),
10 | vuePlugin(),
11 | {
12 | name: 'jsx-query-plugin',
13 | transform(code, id) {
14 | if (id.includes('?query=true')) {
15 | return `
16 | import { createVNode as _createVNode } from "vue";
17 | import { defineComponent, ref } from 'vue';
18 | export default defineComponent(() => {
19 | const count = ref(6);
20 |
21 | const inc = () => count.value++;
22 |
23 | return () => _createVNode("button", {
24 | "class": "jsx-with-query",
25 | "onClick": inc
26 | }, [count.value]);
27 | });
28 | `
29 | }
30 | },
31 | },
32 | ],
33 | build: {
34 | // to make tests faster
35 | minify: false,
36 | },
37 | optimizeDeps: {
38 | disabled: true,
39 | },
40 | })
41 |
--------------------------------------------------------------------------------
/playground/vue-legacy/Main.vue:
--------------------------------------------------------------------------------
1 |
16 |
17 |
18 |
Main.vue
19 |
20 |
21 | {{ css }}
22 |
23 |
24 |
25 |
33 |
--------------------------------------------------------------------------------
/playground/vue-legacy/__tests__/vue-legacy.spec.ts:
--------------------------------------------------------------------------------
1 | import { test } from 'vitest'
2 | import { getBg, untilUpdated } from '~utils'
3 |
4 | test('vue legacy assets', async () => {
5 | await untilUpdated(() => getBg('.main'), 'assets/asset', true)
6 | })
7 |
8 | test('async vue legacy assets', async () => {
9 | await untilUpdated(() => getBg('.module'), 'assets/asset', true)
10 | })
11 |
--------------------------------------------------------------------------------
/playground/vue-legacy/assets/asset.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vitejs/vite-plugin-vue/5f43900edd3d73d10a1655be54a33ac46b267fbd/playground/vue-legacy/assets/asset.png
--------------------------------------------------------------------------------
/playground/vue-legacy/env.d.ts:
--------------------------------------------------------------------------------
1 | declare module '*.png'
2 |
--------------------------------------------------------------------------------
/playground/vue-legacy/index.html:
--------------------------------------------------------------------------------
1 |
2 |
8 |
--------------------------------------------------------------------------------
/playground/vue-legacy/inline.css:
--------------------------------------------------------------------------------
1 | .inline-css {
2 | color: #0088ff;
3 | }
4 |
--------------------------------------------------------------------------------
/playground/vue-legacy/module.vue:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
14 |
--------------------------------------------------------------------------------
/playground/vue-legacy/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@vitejs/test-vue-legacy",
3 | "private": true,
4 | "version": "0.0.0",
5 | "type": "module",
6 | "scripts": {
7 | "dev": "vite",
8 | "build": "vite build",
9 | "debug": "node --inspect-brk vite",
10 | "preview": "vite preview"
11 | },
12 | "dependencies": {
13 | "vue": "catalog:"
14 | },
15 | "devDependencies": {
16 | "@vitejs/plugin-vue": "workspace:*",
17 | "@vitejs/plugin-legacy": "^6.1.1"
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/playground/vue-legacy/vite.config.ts:
--------------------------------------------------------------------------------
1 | import path from 'node:path'
2 | import fs from 'node:fs'
3 | import { defineConfig } from 'vite'
4 | import vuePlugin from '@vitejs/plugin-vue'
5 | import legacyPlugin from '@vitejs/plugin-legacy'
6 |
7 | export default defineConfig({
8 | base: '',
9 | resolve: {
10 | alias: {
11 | '@': __dirname,
12 | },
13 | },
14 | plugins: [
15 | legacyPlugin({
16 | targets: ['defaults', 'not IE 11', 'chrome > 48'],
17 | }),
18 | vuePlugin(),
19 | ],
20 | build: {
21 | minify: false,
22 | },
23 | // special test only hook
24 | // for tests, remove `
5 |