Merge branch 'main' into handle-symbol-unscopables-access

This commit is contained in:
edison 2024-10-08 14:37:59 +08:00 committed by GitHub
commit 03ea8ff9bd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
456 changed files with 22962 additions and 10342 deletions

View File

@ -44,7 +44,7 @@ This reverts commit 667ecc1654a317a13331b17617d973392f415f02.
### Full Message Format
A commit message consists of a **header**, **body** and **footer**. The header has a **type**, **scope** and **subject**:
A commit message consists of a **header**, **body** and **footer**. The header has a **type**, **scope** and **subject**:
```
<type>(<scope>): <subject>
@ -74,9 +74,9 @@ The scope could be anything specifying the place of the commit change. For examp
The subject contains a succinct description of the change:
* use the imperative, present tense: "change" not "changed" nor "changes"
* don't capitalize the first letter
* no dot (.) at the end
- use the imperative, present tense: "change" not "changed" nor "changes"
- don't capitalize the first letter
- no dot (.) at the end
### Body

View File

@ -35,7 +35,6 @@ Hi! I'm really excited that you are interested in contributing to Vue.js. Before
Another aspect of it is that large scale stylistic changes result in massive diffs that touch multiple files, adding noise to the git history and makes tracing behavior changes across commits more cumbersome.
### Pull Request Checklist
- Vue core has two primary work branches: `main` and `minor`.
@ -82,7 +81,7 @@ Hi! I'm really excited that you are interested in contributing to Vue.js. Before
## Development Setup
You will need [Node.js](https://nodejs.org) **version 18.12+**, and [PNPM](https://pnpm.io) **version 8+**.
You will need [Node.js](https://nodejs.org) with minimum version as specified in the [`.node-version`](https://github.com/vuejs/core/blob/main/.node-version) file, and [PNPM](https://pnpm.io) with minimum version as specified in the [`"packageManager"` field in `package.json`](https://github.com/vuejs/core/blob/main/package.json#L4).
We also recommend installing [@antfu/ni](https://github.com/antfu/ni) to help switching between repos using different package managers. `ni` also provides the handy `nr` command which running npm scripts easier.
@ -237,7 +236,7 @@ Tests that test against source code are grouped under `nr test-unit`, while test
### `nr test-dts`
Runs `nr build-dts` first, then verify the type tests in `packages/dts-test` are working correctly against the actual built type declarations.
Runs `nr build-dts` first, then verify the type tests in `packages-private/dts-test` are working correctly against the actual built type declarations.
## Project Structure
@ -336,7 +335,7 @@ Test coverage is continuously deployed at https://coverage.vuejs.org. PRs that i
### Testing Type Definition Correctness
Type tests are located in the `packages/dts-test` directory. To run the dts tests, run `nr test-dts`. Note that the type test requires all relevant `*.d.ts` files to be built first (and the script does it for you). Once the `d.ts` files are built and up-to-date, the tests can be re-run by running `nr test-dts-only`.
Type tests are located in the `packages-private/dts-test` directory. To run the dts tests, run `nr test-dts`. Note that the type test requires all relevant `*.d.ts` files to be built first (and the script does it for you). Once the `d.ts` files are built and up-to-date, the tests can be re-run by running `nr test-dts-only`.
## Financial Contribution

View File

@ -80,6 +80,7 @@ Depending on the type of the PR, different considerations need to be taken into
- Make sure it doesn't accidentally cause dev-only or compiler-only code branches to be included in the runtime build. Notable case is that some functions in @vue/shared are compiler-only and should not be used in runtime code, e.g. `isHTMLTag` and `isSVGTag`.
- Performance
- Be careful about code changes in "hot paths", in particular the Virtual DOM renderer (`runtime-core/src/renderer.ts`) and component instantiation code.
- Potential Breakage

View File

@ -17,8 +17,8 @@
{
groupName: 'playground',
matchFileNames: [
'packages/sfc-playground/package.json',
'packages/template-explorer/package.json',
'packages-private/sfc-playground/package.json',
'packages-private/template-explorer/package.json',
],
},
{
@ -28,7 +28,7 @@
},
{
groupName: 'build',
matchPackageNames: ['vite', 'terser'],
matchPackageNames: ['vite', '@swc/core'],
matchPackagePrefixes: ['rollup', 'esbuild', '@rollup', '@vitejs'],
},
{
@ -54,5 +54,13 @@
// pinned
// https://github.com/vuejs/core/commit/a012e39b373f1b6918e5c89856e8f902e1bfa14d
'@rollup/plugin-replace',
// pinned
// only used in example for e2e tests
'marked',
// pinned, 5.0+ has exports issues
// https://github.com/vuejs/core/issues/11603
'entities',
],
}

View File

@ -31,4 +31,4 @@ jobs:
- name: Run prettier
run: pnpm run format
- uses: autofix-ci/action@2891949f3779a1cafafae1523058501de3d4e944
- uses: autofix-ci/action@ff86a557419858bb967097bfc916833f5647fa8c

View File

@ -28,6 +28,6 @@ jobs:
- run: pnpm install
- run: pnpm release --canary --tag minor
- run: pnpm release --canary --publish --tag minor
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

View File

@ -26,6 +26,6 @@ jobs:
- run: pnpm install
- run: pnpm release --canary
- run: pnpm release --canary --publish
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

View File

@ -3,141 +3,40 @@ on:
push:
branches:
- '**'
tags:
- '!**'
pull_request:
branches:
- main
- minor
permissions:
contents: read # to fetch code (actions/checkout)
jobs:
unit-test:
test:
if: ${{ ! startsWith(github.event.head_commit.message, 'release:') && (github.event_name == 'push' || github.event.pull_request.head.repo.full_name != github.repository) }}
uses: ./.github/workflows/test.yml
continuous-release:
if: github.repository == 'vuejs/core'
runs-on: ubuntu-latest
if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name != github.repository
env:
PUPPETEER_SKIP_DOWNLOAD: 'true'
steps:
- uses: actions/checkout@v4
- name: Checkout
uses: actions/checkout@v4
- name: Install pnpm
uses: pnpm/action-setup@v4.0.0
uses: pnpm/action-setup@v4
- name: Install Node.js
uses: actions/setup-node@v4
with:
node-version-file: '.node-version'
registry-url: 'https://registry.npmjs.org'
cache: 'pnpm'
- run: pnpm install
- name: Install deps
run: pnpm install
- name: Run unit tests
run: pnpm run test-unit
- name: Build
run: pnpm build --withTypes
unit-test-windows:
runs-on: windows-latest
if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name != github.repository
env:
PUPPETEER_SKIP_DOWNLOAD: 'true'
steps:
- uses: actions/checkout@v4
- name: Install pnpm
uses: pnpm/action-setup@v4.0.0
- name: Install Node.js
uses: actions/setup-node@v4
with:
node-version-file: '.node-version'
cache: 'pnpm'
- run: pnpm install
- name: Run compiler unit tests
run: pnpm run test-unit compiler
- name: Run ssr unit tests
run: pnpm run test-unit server-renderer
e2e-test:
runs-on: ubuntu-latest
if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name != github.repository
steps:
- uses: actions/checkout@v4
- name: Setup cache for Chromium binary
uses: actions/cache@v4
with:
path: ~/.cache/puppeteer
key: chromium-${{ hashFiles('pnpm-lock.yaml') }}
- name: Install pnpm
uses: pnpm/action-setup@v4.0.0
- name: Install Node.js
uses: actions/setup-node@v4
with:
node-version-file: '.node-version'
cache: 'pnpm'
- run: pnpm install
- run: node node_modules/puppeteer/install.mjs
- name: Run e2e tests
run: pnpm run test-e2e
- name: verify treeshaking
run: node scripts/verify-treeshaking.js
lint-and-test-dts:
runs-on: ubuntu-latest
if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name != github.repository
env:
PUPPETEER_SKIP_DOWNLOAD: 'true'
steps:
- uses: actions/checkout@v4
- name: Install pnpm
uses: pnpm/action-setup@v4.0.0
- name: Install Node.js
uses: actions/setup-node@v4
with:
node-version-file: '.node-version'
cache: 'pnpm'
- run: pnpm install
- name: Run eslint
run: pnpm run lint
- name: Run prettier
run: pnpm run format-check
- name: Run type declaration tests
run: pnpm run test-dts
# benchmarks:
# runs-on: ubuntu-latest
# if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name != github.repository
# env:
# PUPPETEER_SKIP_DOWNLOAD: 'true'
# steps:
# - uses: actions/checkout@v4
# - name: Install pnpm
# uses: pnpm/action-setup@v3.0.0
# - name: Install Node.js
# uses: actions/setup-node@v4
# with:
# node-version-file: '.node-version'
# cache: 'pnpm'
# - run: pnpm install
# - name: Run benchmarks
# uses: CodSpeedHQ/action@v2
# with:
# run: pnpm vitest bench --run
# token: ${{ secrets.CODSPEED_TOKEN }}
- name: Release
run: pnpx pkg-pr-new publish --compact --pnpm './packages/*'

View File

@ -0,0 +1,21 @@
name: Auto close issues with "can't reproduce" label
on:
schedule:
- cron: '0 0 * * *'
permissions:
issues: write
jobs:
close-issues:
if: github.repository == 'vuejs/core'
runs-on: ubuntu-latest
steps:
- name: can't reproduce
uses: actions-cool/issues-helper@v3
with:
actions: 'close-issues'
token: ${{ secrets.GITHUB_TOKEN }}
labels: "can't reproduce"
inactive-day: 3

View File

@ -9,7 +9,8 @@ jobs:
runs-on: ubuntu-latest
if: github.repository == 'vuejs/core' && github.event.issue.pull_request && startsWith(github.event.comment.body, '/ecosystem-ci run')
steps:
- uses: actions/github-script@v7
- name: Check user permission
uses: actions/github-script@v7
with:
script: |
const user = context.payload.sender.login
@ -43,7 +44,8 @@ jobs:
})
throw new Error('not allowed')
}
- uses: actions/github-script@v7
- name: Get PR info
uses: actions/github-script@v7
id: get-pr-data
with:
script: |
@ -56,9 +58,11 @@ jobs:
return {
num: context.issue.number,
branchName: pr.head.ref,
repo: pr.head.repo.full_name
repo: pr.head.repo.full_name,
commit: pr.head.sha
}
- uses: actions/github-script@v7
- name: Trigger run
uses: actions/github-script@v7
id: trigger
env:
COMMENT: ${{ github.event.comment.body }}
@ -80,6 +84,7 @@ jobs:
prNumber: '' + prData.num,
branchName: prData.branchName,
repo: prData.repo,
suite: suite === '' ? '-' : suite
suite: suite === '' ? '-' : suite,
commit: prData.commit
}
})

View File

@ -1,28 +0,0 @@
on:
push:
tags:
- 'v*' # Push events to matching v*, i.e. v1.0, v20.15.10
name: Create Release
permissions: {}
jobs:
build:
permissions:
contents: write # to create release (yyx990803/release-tag)
name: Create Release
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@master
- name: Create Release for Tag
id: release_tag
uses: yyx990803/release-tag@master
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ github.ref }}
body: |
For stable releases, please refer to [CHANGELOG.md](https://github.com/vuejs/core/blob/main/CHANGELOG.md) for details.
For pre-releases, please refer to [CHANGELOG.md](https://github.com/vuejs/core/blob/minor/CHANGELOG.md) of the `minor` branch.

55
.github/workflows/release.yml vendored Normal file
View File

@ -0,0 +1,55 @@
name: Release
on:
push:
tags:
- 'v*' # Push events to matching v*, i.e. v1.0, v20.15.10
jobs:
test:
uses: ./.github/workflows/test.yml
release:
# prevents this action from running on forks
if: github.repository == 'vuejs/core'
needs: [test]
runs-on: ubuntu-latest
permissions:
contents: write
id-token: write
# Use Release environment for deployment protection
environment: Release
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install pnpm
uses: pnpm/action-setup@v4
- name: Install Node.js
uses: actions/setup-node@v4
with:
node-version-file: '.node-version'
registry-url: 'https://registry.npmjs.org'
cache: 'pnpm'
- name: Install deps
run: pnpm install
- name: Build and publish
id: publish
run: |
pnpm release --publishOnly
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
- name: Create GitHub release
id: release_tag
uses: yyx990803/release-tag@master
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ github.ref }}
body: |
For stable releases, please refer to [CHANGELOG.md](https://github.com/vuejs/core/blob/main/CHANGELOG.md) for details.
For pre-releases, please refer to [CHANGELOG.md](https://github.com/vuejs/core/blob/minor/CHANGELOG.md) of the `minor` branch.

View File

@ -4,6 +4,7 @@ on:
push:
branches:
- main
- minor
pull_request:
branches:
- main
@ -17,6 +18,7 @@ env:
jobs:
upload:
if: github.repository == 'vuejs/core'
runs-on: ubuntu-latest
steps:
@ -36,18 +38,14 @@ jobs:
- run: pnpm run size
- name: Save PR number & base branch
if: ${{github.event_name == 'pull_request'}}
run: |
echo ${{ github.event.number }} > ./temp/size/number.txt
echo ${{ github.base_ref }} > ./temp/size/base.txt
- name: Upload Size Data
uses: actions/upload-artifact@v4
with:
name: size-data
path: temp/size
- name: Save PR number
if: ${{github.event_name == 'pull_request'}}
run: echo ${{ github.event.number }} > ./pr.txt
- uses: actions/upload-artifact@v4
if: ${{github.event_name == 'pull_request'}}
with:
name: pr-number
path: pr.txt

View File

@ -18,6 +18,7 @@ jobs:
size-report:
runs-on: ubuntu-latest
if: >
github.repository == 'vuejs/core' &&
github.event.workflow_run.event == 'pull_request' &&
github.event.workflow_run.conclusion == 'success'
steps:
@ -35,19 +36,6 @@ jobs:
- name: Install dependencies
run: pnpm install
- name: Download PR number
uses: dawidd6/action-download-artifact@v6
with:
name: pr-number
run_id: ${{ github.event.workflow_run.id }}
path: /tmp/pr-number
- name: Read PR Number
id: pr-number
uses: juliangruber/read-file-action@v1
with:
path: /tmp/pr-number/pr.txt
- name: Download Size Data
uses: dawidd6/action-download-artifact@v6
with:
@ -55,10 +43,22 @@ jobs:
run_id: ${{ github.event.workflow_run.id }}
path: temp/size
- name: Read PR Number
id: pr-number
uses: juliangruber/read-file-action@v1
with:
path: temp/size/number.txt
- name: Read base branch
id: pr-base
uses: juliangruber/read-file-action@v1
with:
path: temp/size/base.txt
- name: Download Previous Size Data
uses: dawidd6/action-download-artifact@v6
with:
branch: main
branch: ${{ steps.pr-base.outputs.content }}
workflow: size-data.yml
event: push
name: size-data
@ -66,7 +66,7 @@ jobs:
if_no_artifact_found: warn
- name: Prepare report
run: pnpm tsx scripts/size-report.ts > size-report.md
run: node scripts/size-report.js > size-report.md
- name: Read Size Report
id: size-report

108
.github/workflows/test.yml vendored Normal file
View File

@ -0,0 +1,108 @@
name: 'test'
on: workflow_call
permissions:
contents: read # to fetch code (actions/checkout)
jobs:
unit-test:
runs-on: ubuntu-latest
env:
PUPPETEER_SKIP_DOWNLOAD: 'true'
steps:
- uses: actions/checkout@v4
- name: Install pnpm
uses: pnpm/action-setup@v4.0.0
- name: Install Node.js
uses: actions/setup-node@v4
with:
node-version-file: '.node-version'
cache: 'pnpm'
- run: pnpm install
- name: Run unit tests
run: pnpm run test-unit
unit-test-windows:
runs-on: windows-latest
env:
PUPPETEER_SKIP_DOWNLOAD: 'true'
steps:
- uses: actions/checkout@v4
- name: Install pnpm
uses: pnpm/action-setup@v4.0.0
- name: Install Node.js
uses: actions/setup-node@v4
with:
node-version-file: '.node-version'
cache: 'pnpm'
- run: pnpm install
- name: Run compiler unit tests
run: pnpm run test-unit compiler
- name: Run ssr unit tests
run: pnpm run test-unit server-renderer
e2e-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup cache for Chromium binary
uses: actions/cache@v4
with:
path: ~/.cache/puppeteer
key: chromium-${{ hashFiles('pnpm-lock.yaml') }}
- name: Install pnpm
uses: pnpm/action-setup@v4.0.0
- name: Install Node.js
uses: actions/setup-node@v4
with:
node-version-file: '.node-version'
cache: 'pnpm'
- run: pnpm install
- run: node node_modules/puppeteer/install.mjs
- name: Run e2e tests
run: pnpm run test-e2e
- name: verify treeshaking
run: node scripts/verify-treeshaking.js
lint-and-test-dts:
runs-on: ubuntu-latest
env:
PUPPETEER_SKIP_DOWNLOAD: 'true'
steps:
- uses: actions/checkout@v4
- name: Install pnpm
uses: pnpm/action-setup@v4.0.0
- name: Install Node.js
uses: actions/setup-node@v4
with:
node-version-file: '.node-version'
cache: 'pnpm'
- run: pnpm install
- name: Run eslint
run: pnpm run lint
- name: Run prettier
run: pnpm run format-check
- name: Run type declaration tests
run: pnpm run test-dts

View File

@ -1,4 +1,3 @@
dist
*.md
*.html
pnpm-lock.yaml
CHANGELOG*.md

3
.vscode/extensions.json vendored Normal file
View File

@ -0,0 +1,3 @@
{
"recommendations": ["vitest.explorer"]
}

File diff suppressed because it is too large Load Diff

View File

@ -10,6 +10,6 @@ Please note that we do not consider XSS via template expressions a valid attack
We would like to thank the following security researchers for responsibly disclosing security issues to us.
- Jeet Pal - [@jeetpal2007](https://github.com/jeetpal2007) | [Email](jeetpal2007@gmail.com) | [LinkedIn](https://in.linkedin.com/in/jeet-pal-22601a290 )
- Jeet Pal - [@jeetpal2007](https://github.com/jeetpal2007) | [Email](mailto:jeetpal2007@gmail.com) | [LinkedIn](https://in.linkedin.com/in/jeet-pal-22601a290)
- Mix - [@mnixry](https://github.com/mnixry)
- Aviv Keller - [@RedYetiDev](https://github.com/redyetidev) | [LinkedIn](https://www.linkedin.com/in/redyetidev) <redyetidev@gmail.com>

1021
changelogs/CHANGELOG-3.4.md Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
import importX from 'eslint-plugin-import-x'
import tseslint from 'typescript-eslint'
import vitest from 'eslint-plugin-vitest'
import vitest from '@vitest/eslint-plugin'
import { builtinModules } from 'node:module'
const DOMGlobals = ['window', 'document']
@ -76,7 +76,11 @@ export default tseslint.config(
// tests, no restrictions (runs in Node / Vitest with jsdom)
{
files: ['**/__tests__/**', 'packages/dts-test/**'],
files: [
'**/__tests__/**',
'packages-private/dts-test/**',
'packages-private/dts-build-test/**',
],
plugins: { vitest },
languageOptions: {
globals: {
@ -119,7 +123,10 @@ export default tseslint.config(
// Private package, browser only + no syntax restrictions
{
files: ['packages/template-explorer/**', 'packages/sfc-playground/**'],
files: [
'packages-private/template-explorer/**',
'packages-private/sfc-playground/**',
],
rules: {
'no-restricted-globals': ['error', ...NodeGlobals],
'no-restricted-syntax': ['error', banConstEnum],

View File

@ -1,14 +1,14 @@
{
"private": true,
"version": "3.4.31",
"packageManager": "pnpm@9.5.0",
"version": "3.5.11",
"packageManager": "pnpm@9.12.0",
"type": "module",
"scripts": {
"dev": "node scripts/dev.js",
"build": "node scripts/build.js",
"build-dts": "tsc -p tsconfig.build-browser.json && tsc -p tsconfig.build-node.json && rollup -c rollup.dts.config.js",
"clean": "rimraf packages/*/dist temp .eslintcache",
"size": "run-s \"size-*\" && tsx scripts/usage-size.ts",
"build-dts": "tsc -p tsconfig.build.json --noCheck && rollup -c rollup.dts.config.js",
"clean": "rimraf --glob packages/*/dist temp .eslintcache",
"size": "run-s \"size-*\" && node scripts/usage-size.js",
"size-global": "node scripts/build.js vue runtime-dom -f global -p --size",
"size-esm-runtime": "node scripts/build.js vue -f esm-bundler-runtime",
"size-esm": "node scripts/build.js runtime-dom runtime-core reactivity shared -f esm-bundler",
@ -17,11 +17,11 @@
"format": "prettier --write --cache .",
"format-check": "prettier --check --cache .",
"test": "vitest",
"test-unit": "vitest -c vitest.unit.config.ts",
"test-e2e": "node scripts/build.js vue -f global -d && vitest -c vitest.e2e.config.ts",
"test-unit": "vitest --project unit",
"test-e2e": "node scripts/build.js vue -f global -d && vitest --project e2e",
"test-dts": "run-s build-dts test-dts-only",
"test-dts-only": "tsc -p packages/dts-built-test/tsconfig.json && tsc -p ./packages/dts-test/tsconfig.test.json",
"test-coverage": "vitest -c vitest.unit.config.ts --coverage",
"test-dts-only": "tsc -p packages-private/dts-built-test/tsconfig.json && tsc -p ./packages-private/dts-test/tsconfig.test.json",
"test-coverage": "vitest run --project unit --coverage",
"test-bench": "vitest bench",
"release": "node scripts/release.js",
"changelog": "conventional-changelog -p angular -i CHANGELOG.md -s",
@ -29,16 +29,16 @@
"dev-compiler": "run-p \"dev template-explorer\" serve",
"dev-sfc": "run-s dev-sfc-prepare dev-sfc-run",
"dev-sfc-prepare": "node scripts/pre-dev-sfc.js || npm run build-all-cjs",
"dev-sfc-serve": "vite packages/sfc-playground --host",
"dev-sfc-serve": "vite packages-private/sfc-playground --host",
"dev-sfc-run": "run-p \"dev compiler-sfc -f esm-browser\" \"dev vue -if esm-bundler-runtime\" \"dev vue -ipf esm-browser-runtime\" \"dev server-renderer -if esm-bundler\" dev-sfc-serve",
"serve": "serve",
"open": "open http://localhost:3000/packages/template-explorer/local.html",
"open": "open http://localhost:3000/packages-private/template-explorer/local.html",
"build-sfc-playground": "run-s build-all-cjs build-runtime-esm build-browser-esm build-ssr-esm build-sfc-playground-self",
"build-all-cjs": "node scripts/build.js vue runtime compiler reactivity shared -af cjs",
"build-runtime-esm": "node scripts/build.js runtime reactivity shared -af esm-bundler && node scripts/build.js vue -f esm-bundler-runtime && node scripts/build.js vue -f esm-browser-runtime",
"build-browser-esm": "node scripts/build.js runtime reactivity shared -af esm-bundler && node scripts/build.js vue -f esm-bundler && node scripts/build.js vue -f esm-browser",
"build-ssr-esm": "node scripts/build.js compiler-sfc server-renderer -f esm-browser",
"build-sfc-playground-self": "cd packages/sfc-playground && npm run build",
"build-sfc-playground-self": "cd packages-private/sfc-playground && npm run build",
"preinstall": "npx only-allow pnpm",
"postinstall": "simple-git-hooks"
},
@ -61,54 +61,53 @@
"devDependencies": {
"@babel/parser": "catalog:",
"@babel/types": "catalog:",
"@codspeed/vitest-plugin": "^3.1.0",
"@rollup/plugin-alias": "^5.1.0",
"@rollup/plugin-commonjs": "^26.0.1",
"@rollup/plugin-alias": "^5.1.1",
"@rollup/plugin-commonjs": "^28.0.0",
"@rollup/plugin-json": "^6.1.0",
"@rollup/plugin-node-resolve": "^15.2.3",
"@rollup/plugin-node-resolve": "^15.3.0",
"@rollup/plugin-replace": "5.0.4",
"@swc/core": "^1.6.13",
"@swc/core": "^1.7.28",
"@types/hash-sum": "^1.0.2",
"@types/node": "^20.14.10",
"@types/node": "^20.16.10",
"@types/semver": "^7.5.8",
"@vitest/coverage-istanbul": "^1.6.0",
"@types/serve-handler": "^6.1.4",
"@vitest/coverage-v8": "^2.1.1",
"@vue/consolidate": "1.0.0",
"conventional-changelog-cli": "^4.1.0",
"conventional-changelog-cli": "^5.0.0",
"enquirer": "^2.4.1",
"esbuild": "^0.23.0",
"esbuild": "^0.24.0",
"esbuild-plugin-polyfill-node": "^0.3.0",
"eslint": "^9.6.0",
"eslint-plugin-import-x": "^0.5.3",
"eslint-plugin-vitest": "^0.5.4",
"estree-walker": "^2.0.2",
"jsdom": "^24.1.0",
"lint-staged": "^15.2.7",
"eslint": "^9.12.0",
"eslint-plugin-import-x": "^4.3.1",
"@vitest/eslint-plugin": "^1.0.1",
"estree-walker": "catalog:",
"jsdom": "^25.0.0",
"lint-staged": "^15.2.10",
"lodash": "^4.17.21",
"magic-string": "^0.30.10",
"magic-string": "^0.30.11",
"markdown-table": "^3.0.3",
"marked": "^12.0.2",
"npm-run-all2": "^6.2.2",
"picocolors": "^1.0.1",
"prettier": "^3.3.2",
"marked": "13.0.3",
"npm-run-all2": "^6.2.3",
"picocolors": "^1.1.0",
"prettier": "^3.3.3",
"pretty-bytes": "^6.1.1",
"pug": "^3.0.3",
"puppeteer": "~22.12.1",
"rimraf": "^5.0.8",
"rollup": "^4.18.0",
"puppeteer": "~23.3.0",
"rimraf": "^6.0.1",
"rollup": "^4.24.0",
"rollup-plugin-dts": "^6.1.1",
"rollup-plugin-esbuild": "^6.1.1",
"rollup-plugin-polyfill-node": "^0.13.0",
"semver": "^7.6.2",
"semver": "^7.6.3",
"serve": "^14.2.3",
"serve-handler": "^6.1.5",
"simple-git-hooks": "^2.11.1",
"terser": "^5.31.1",
"todomvc-app-css": "^2.4.3",
"tslib": "^2.6.3",
"tsx": "^4.16.2",
"typescript": "~5.4.5",
"typescript-eslint": "^7.15.0",
"tslib": "^2.7.0",
"typescript": "~5.6.2",
"typescript-eslint": "^8.8.0",
"vite": "catalog:",
"vitest": "^1.6.0"
"vitest": "^2.1.1"
},
"pnpm": {
"peerDependencyRules": {

View File

@ -2,4 +2,4 @@
This package is private and for testing only. It is used to verify edge cases for external libraries that build their types using Vue core types - e.g. Vuetify as in [#8376](https://github.com/vuejs/core/issues/8376).
When running the `build-dts` task, this package's types are built alongside other packages. Then, during `test-dts-only` it is imported and used in [`packages/dts-test/built.test-d.ts`](https://github.com/vuejs/core/blob/main/packages/dts-test/built.test-d.ts) to verify that the built types work correctly.
When running the `build-dts` task, this package's types are built alongside other packages. Then, during `test-dts-only` it is imported and used in [`packages-private/dts-test/built.test-d.ts`](https://github.com/vuejs/core/blob/main/packages-private/dts-test/built.test-d.ts) to verify that the built types work correctly.

View File

@ -1,5 +1,5 @@
{
"name": "@vue/dts-built-test",
"name": "dts-built-test",
"private": true,
"version": "0.0.0",
"types": "dist/index.d.ts",

View File

@ -4,4 +4,4 @@ Tests TypeScript types to ensure the types remain as expected.
- This directory is included in the root `tsconfig.json`, where package imports are aliased to `src` directories, so in IDEs and the `pnpm check` script the types are validated against source code.
- When running `tsc` with `packages/dts-test/tsconfig.test.json`, packages are resolved using normal `node` resolution, so the types are validated against actual **built** types. This requires the types to be built first via `pnpm build-types`.
- When running `tsc` with `packages-private/dts-test/tsconfig.test.json`, packages are resolved using normal `node` resolution, so the types are validated against actual **built** types. This requires the types to be built first via `pnpm build-dts`.

View File

@ -0,0 +1,19 @@
import { createApp } from 'vue'
import { expectType } from './utils'
const app = createApp({})
app.directive<HTMLElement, string, 'prevent' | 'stop', 'arg1' | 'arg2'>(
'custom',
{
mounted(el, binding) {
expectType<HTMLElement>(el)
expectType<string>(binding.value)
expectType<{ prevent: boolean; stop: boolean }>(binding.modifiers)
expectType<'arg1' | 'arg2'>(binding.arg!)
// @ts-expect-error not any
expectType<number>(binding.value)
},
},
)

View File

@ -1,4 +1,4 @@
import { CustomPropsNotErased } from '@vue/dts-built-test'
import { CustomPropsNotErased } from 'dts-built-test/src/index'
import { describe, expectType } from './utils'
declare module 'vue' {

View File

@ -1,4 +1,4 @@
import { defineComponent } from 'vue'
import { type DefineComponent, type Directive, defineComponent } from 'vue'
import { expectType } from './utils'
declare module 'vue' {
@ -6,6 +6,14 @@ declare module 'vue' {
test?(n: number): void
}
interface GlobalDirectives {
test: Directive
}
interface GlobalComponents {
RouterView: DefineComponent<{}>
}
interface ComponentCustomProperties {
state?: 'stopped' | 'running'
}
@ -46,6 +54,8 @@ export const Custom = defineComponent({
},
})
expectType<Directive>(Custom.directives!.test)
expectType<DefineComponent<{}>>(Custom.components!.RouterView)
expectType<JSX.Element>(<Custom baz={1} />)
expectType<JSX.Element>(<Custom custom={1} baz={1} />)
expectType<JSX.Element>(<Custom bar="bar" baz={1} />)

View File

@ -15,7 +15,7 @@ import {
withKeys,
withModifiers,
} from 'vue'
import { type IsUnion, describe, expectType } from './utils'
import { type IsAny, type IsUnion, describe, expectType } from './utils'
describe('with object props', () => {
interface ExpectedProps {
@ -480,6 +480,26 @@ describe('type inference w/ options API', () => {
})
})
// #4051
describe('type inference w/ empty prop object', () => {
const MyComponent = defineComponent({
props: {},
setup(props) {
return {}
},
render() {},
})
expectType<JSX.Element>(<MyComponent />)
// AllowedComponentProps
expectType<JSX.Element>(<MyComponent class={'foo'} />)
// ComponentCustomProps
expectType<JSX.Element>(<MyComponent custom={1} />)
// VNodeProps
expectType<JSX.Element>(<MyComponent key="1" />)
// @ts-expect-error
expectError(<MyComponent other="other" />)
})
describe('with mixins', () => {
const MixinA = defineComponent({
emits: ['bar'],
@ -906,12 +926,15 @@ describe('emits', () => {
emits: {
click: (n: number) => typeof n === 'number',
input: (b: string) => b.length > 1,
Focus: (f: boolean) => !!f,
},
setup(props, { emit }) {
expectType<((n: number) => boolean) | undefined>(props.onClick)
expectType<((b: string) => boolean) | undefined>(props.onInput)
expectType<((f: boolean) => boolean) | undefined>(props.onFocus)
emit('click', 1)
emit('input', 'foo')
emit('Focus', true)
// @ts-expect-error
emit('nope')
// @ts-expect-error
@ -922,6 +945,10 @@ describe('emits', () => {
emit('input')
// @ts-expect-error
emit('input', 1)
// @ts-expect-error
emit('focus')
// @ts-expect-error
emit('focus', true)
},
created() {
this.$emit('click', 1)
@ -936,6 +963,10 @@ describe('emits', () => {
this.$emit('input')
// @ts-expect-error
this.$emit('input', 1)
// @ts-expect-error
this.$emit('focus')
// @ts-expect-error
this.$emit('focus', true)
},
mounted() {
// #3599
@ -954,6 +985,10 @@ describe('emits', () => {
this.$emit('input')
// @ts-expect-error
this.$emit('input', 1)
// @ts-expect-error
this.$emit('focus')
// @ts-expect-error
this.$emit('focus', true)
})
},
})
@ -1006,6 +1041,18 @@ describe('emits', () => {
},
})
// #11803 manual props annotation in setup()
const Hello = defineComponent({
name: 'HelloWorld',
inheritAttrs: false,
props: { foo: String },
emits: {
customClick: (args: string) => typeof args === 'string',
},
setup(props: { foo?: string }) {},
})
;<Hello onCustomClick={() => {}} />
// without emits
defineComponent({
setup(props, { emit }) {
@ -1505,14 +1552,104 @@ describe('withKeys and withModifiers as pro', () => {
;<input onKeydown={onKeydown} onClick={onClick} />
})
// #3367 expose components types
describe('expose component types', () => {
const child = defineComponent({
props: {
a: String,
},
})
const parent = defineComponent({
components: {
child,
child2: {
template: `<div></div>`,
},
},
})
expectType<typeof child>(parent.components!.child)
expectType<Component>(parent.components!.child2)
// global components
expectType<Readonly<KeepAliveProps>>(
new parent.components!.KeepAlive().$props,
)
expectType<Readonly<KeepAliveProps>>(new child.components!.KeepAlive().$props)
// runtime-dom components
expectType<Readonly<TransitionProps>>(
new parent.components!.Transition().$props,
)
expectType<Readonly<TransitionProps>>(
new child.components!.Transition().$props,
)
})
describe('directive typing', () => {
const customDirective: Directive = {
created(_) {},
}
const comp = defineComponent({
props: {
a: String,
},
directives: {
customDirective,
localDirective: {
created(_, { arg }) {
expectType<string | undefined>(arg)
},
},
},
})
expectType<typeof customDirective>(comp.directives!.customDirective)
expectType<Directive>(comp.directives!.localDirective)
// global directive
expectType<typeof vShow>(comp.directives!.vShow)
})
describe('expose typing', () => {
const Comp = defineComponent({
expose: ['a', 'b'],
props: {
some: String,
},
data() {
return { a: 1, b: '2', c: 1 }
},
})
expectType<Array<'a' | 'b'>>(Comp.expose!)
const vm = new Comp()
// internal should still be exposed
vm.$props
expectType<number>(vm.a)
expectType<string>(vm.b)
// @ts-expect-error shouldn't be exposed
vm.c
})
import type {
AllowedComponentProps,
ComponentCustomProps,
ComponentInstance,
ComponentOptionsMixin,
DefineComponent,
Directive,
EmitsOptions,
ExtractPropTypes,
KeepAliveProps,
TransitionProps,
VNodeProps,
vShow,
} from 'vue'
// code generated by tsc / vue-tsc, make sure this continues to work
@ -1533,3 +1670,401 @@ declare const MyButton: DefineComponent<
{}
>
;<MyButton class="x" />
describe('__typeProps backdoor for union type for conditional props', () => {
interface CommonProps {
size?: 'xl' | 'l' | 'm' | 's' | 'xs'
}
type ConditionalProps =
| {
color?: 'normal' | 'primary' | 'secondary'
appearance?: 'normal' | 'outline' | 'text'
}
| {
color: 'white'
appearance: 'outline'
}
type Props = CommonProps & ConditionalProps
const Comp = defineComponent({
__typeProps: {} as Props,
})
// @ts-expect-error
;<Comp color="white" />
// @ts-expect-error
;<Comp color="white" appearance="normal" />
;<Comp color="white" appearance="outline" />
const c = new Comp()
// @ts-expect-error
c.$props = { color: 'white' }
// @ts-expect-error
c.$props = { color: 'white', appearance: 'text' }
c.$props = { color: 'white', appearance: 'outline' }
})
describe('__typeEmits backdoor, 3.3+ object syntax', () => {
type Emits = {
change: [id: number]
update: [value: string]
}
const Comp = defineComponent({
__typeEmits: {} as Emits,
mounted() {
this.$props.onChange?.(123)
// @ts-expect-error
this.$props.onChange?.('123')
this.$props.onUpdate?.('foo')
// @ts-expect-error
this.$props.onUpdate?.(123)
// @ts-expect-error
this.$emit('foo')
this.$emit('change', 123)
// @ts-expect-error
this.$emit('change', '123')
this.$emit('update', 'test')
// @ts-expect-error
this.$emit('update', 123)
},
})
;<Comp onChange={id => id.toFixed(2)} />
;<Comp onUpdate={id => id.toUpperCase()} />
// @ts-expect-error
;<Comp onChange={id => id.slice(1)} />
// @ts-expect-error
;<Comp onUpdate={id => id.toFixed(2)} />
const c = new Comp()
// @ts-expect-error
c.$emit('foo')
c.$emit('change', 123)
// @ts-expect-error
c.$emit('change', '123')
c.$emit('update', 'test')
// @ts-expect-error
c.$emit('update', 123)
})
describe('__typeEmits backdoor, call signature syntax', () => {
type Emits = {
(e: 'change', id: number): void
(e: 'update', value: string): void
}
const Comp = defineComponent({
__typeEmits: {} as Emits,
mounted() {
this.$props.onChange?.(123)
// @ts-expect-error
this.$props.onChange?.('123')
this.$props.onUpdate?.('foo')
// @ts-expect-error
this.$props.onUpdate?.(123)
// @ts-expect-error
this.$emit('foo')
this.$emit('change', 123)
// @ts-expect-error
this.$emit('change', '123')
this.$emit('update', 'test')
// @ts-expect-error
this.$emit('update', 123)
},
})
;<Comp onChange={id => id.toFixed(2)} />
;<Comp onUpdate={id => id.toUpperCase()} />
// @ts-expect-error
;<Comp onChange={id => id.slice(1)} />
// @ts-expect-error
;<Comp onUpdate={id => id.toFixed(2)} />
const c = new Comp()
// @ts-expect-error
c.$emit('foo')
c.$emit('change', 123)
// @ts-expect-error
c.$emit('change', '123')
c.$emit('update', 'test')
// @ts-expect-error
c.$emit('update', 123)
})
describe('__typeRefs backdoor, object syntax', () => {
type Refs = {
foo: number
}
const Parent = defineComponent({
__typeRefs: {} as { child: ComponentInstance<typeof Child> },
})
const Child = defineComponent({
__typeRefs: {} as Refs,
})
const c = new Parent()
const refs = c.$refs
expectType<ComponentInstance<typeof Child>>(refs.child)
expectType<number>(refs.child.$refs.foo)
})
describe('__typeEl backdoor', () => {
const Comp = defineComponent({
__typeEl: {} as HTMLAnchorElement,
})
const c = new Comp()
expectType<HTMLAnchorElement>(c.$el)
})
defineComponent({
props: {
foo: [String, null],
},
setup(props) {
expectType<IsAny<typeof props.foo>>(false)
expectType<string | null | undefined>(props.foo)
},
})
import type * as vue from 'vue'
interface ErrorMessageSlotProps {
message: string | undefined
}
/**
* #10842
* component types generated by vue-tsc
* relying on legacy CreateComponentPublicInstance signature
*/
declare const ErrorMessage: {
new (...args: any[]): vue.CreateComponentPublicInstance<
Readonly<
vue.ExtractPropTypes<{
as: {
type: StringConstructor
default: any
}
name: {
type: StringConstructor
required: true
}
}>
>,
() =>
| VNode<
vue.RendererNode,
vue.RendererElement,
{
[key: string]: any
}
>
| vue.Slot<any>
| VNode<
vue.RendererNode,
vue.RendererElement,
{
[key: string]: any
}
>[]
| {
default: () => VNode<
vue.RendererNode,
vue.RendererElement,
{
[key: string]: any
}
>[]
},
unknown,
{},
{},
vue.ComponentOptionsMixin,
vue.ComponentOptionsMixin,
{},
vue.VNodeProps &
vue.AllowedComponentProps &
vue.ComponentCustomProps &
Readonly<
vue.ExtractPropTypes<{
as: {
type: StringConstructor
default: any
}
name: {
type: StringConstructor
required: true
}
}>
>,
{
as: string
},
true,
{},
{},
{
P: {}
B: {}
D: {}
C: {}
M: {}
Defaults: {}
},
Readonly<
vue.ExtractPropTypes<{
as: {
type: StringConstructor
default: any
}
name: {
type: StringConstructor
required: true
}
}>
>,
() =>
| VNode<
vue.RendererNode,
vue.RendererElement,
{
[key: string]: any
}
>
| vue.Slot<any>
| VNode<
vue.RendererNode,
vue.RendererElement,
{
[key: string]: any
}
>[]
| {
default: () => VNode<
vue.RendererNode,
vue.RendererElement,
{
[key: string]: any
}
>[]
},
{},
{},
{},
{
as: string
}
>
__isFragment?: never
__isTeleport?: never
__isSuspense?: never
} & vue.ComponentOptionsBase<
Readonly<
vue.ExtractPropTypes<{
as: {
type: StringConstructor
default: any
}
name: {
type: StringConstructor
required: true
}
}>
>,
() =>
| VNode<
vue.RendererNode,
vue.RendererElement,
{
[key: string]: any
}
>
| vue.Slot<any>
| VNode<
vue.RendererNode,
vue.RendererElement,
{
[key: string]: any
}
>[]
| {
default: () => VNode<
vue.RendererNode,
vue.RendererElement,
{
[key: string]: any
}
>[]
},
unknown,
{},
{},
vue.ComponentOptionsMixin,
vue.ComponentOptionsMixin,
{},
string,
{
as: string
},
{},
string,
{}
> &
vue.VNodeProps &
vue.AllowedComponentProps &
vue.ComponentCustomProps &
(new () => {
$slots: {
default: (arg: ErrorMessageSlotProps) => VNode[]
}
})
;<ErrorMessage name="password" class="error" />
// #10843
createApp({}).component(
'SomeComponent',
defineComponent({
props: {
title: String,
},
setup(props) {
expectType<string | undefined>(props.title)
return {}
},
}),
)
const Comp = defineComponent({
props: {
actionText: {
type: {} as PropType<string>,
default: 'Become a sponsor',
},
},
__typeProps: {} as {
actionText?: string
},
})
const instance = new Comp()
function expectString(s: string) {}
// instance prop with default should be non-null
expectString(instance.actionText)
// public prop on $props should be optional
// @ts-expect-error
expectString(instance.$props.actionText)

View File

@ -68,7 +68,7 @@ describe('inject', () => {
})
describe('defineCustomElement using defineComponent return type', () => {
test('with emits', () => {
test('with object emits', () => {
const Comp1Vue = defineComponent({
props: {
a: String,
@ -80,6 +80,56 @@ describe('defineCustomElement using defineComponent return type', () => {
const Comp = defineCustomElement(Comp1Vue)
expectType<VueElementConstructor>(Comp)
expectType<string | undefined>(new Comp().a)
const instance = new Comp()
expectType<string | undefined>(instance.a)
instance.a = ''
})
test('with array emits', () => {
const Comp1Vue = defineComponent({
props: {
a: Number,
},
emits: ['click'],
})
const Comp = defineCustomElement(Comp1Vue)
expectType<VueElementConstructor>(Comp)
const instance = new Comp()
expectType<number | undefined>(instance.a)
instance.a = 42
})
test('with required props', () => {
const Comp1Vue = defineComponent({
props: {
a: { type: Number, required: true },
},
})
const Comp = defineCustomElement(Comp1Vue)
expectType<VueElementConstructor>(Comp)
const instance = new Comp()
expectType<number>(instance.a)
instance.a = 42
})
test('with default props', () => {
const Comp1Vue = defineComponent({
props: {
a: {
type: Number,
default: 1,
validator: () => true,
},
},
emits: ['click'],
})
const Comp = defineCustomElement(Comp1Vue)
expectType<VueElementConstructor>(Comp)
const instance = new Comp()
expectType<number>(instance.a)
instance.a = 42
})
})

View File

@ -0,0 +1,58 @@
import { type Directive, type ObjectDirective, vModelText } from 'vue'
import { describe, expectType } from './utils'
type ExtractBinding<T> = T extends (
el: any,
binding: infer B,
vnode: any,
prev: any,
) => any
? B
: never
declare function testDirective<
Value,
Modifiers extends string = string,
Arg extends string = string,
>(): ExtractBinding<Directive<any, Value, Modifiers, Arg>>
describe('vmodel', () => {
expectType<ObjectDirective<any, any, 'trim' | 'number' | 'lazy', string>>(
vModelText,
)
// @ts-expect-error
expectType<ObjectDirective<any, any, 'not-valid', string>>(vModelText)
})
describe('custom', () => {
expectType<{
value: number
oldValue: number | null
arg?: 'Arg'
modifiers: Record<'a' | 'b', boolean>
}>(testDirective<number, 'a' | 'b', 'Arg'>())
expectType<{
value: number
oldValue: number | null
arg?: 'Arg'
modifiers: Record<'a' | 'b', boolean>
// @ts-expect-error
}>(testDirective<number, 'a', 'Arg'>())
expectType<{
value: number
oldValue: number | null
arg?: 'Arg'
modifiers: Record<'a' | 'b', boolean>
// @ts-expect-error
}>(testDirective<number, 'a' | 'b', 'Argx'>())
expectType<{
value: number
oldValue: number | null
arg?: 'Arg'
modifiers: Record<'a' | 'b', boolean>
// @ts-expect-error
}>(testDirective<string, 'a' | 'b', 'Arg'>())
})

View File

@ -2,6 +2,7 @@ import {
type InjectionKey,
type Ref,
createApp,
defineComponent,
inject,
provide,
ref,
@ -52,3 +53,9 @@ provide<Cube>(123, { size: 'foo' })
const app = createApp({})
// @ts-expect-error
app.provide(injectionKeyRef, ref({}))
defineComponent({
provide: {
[injectionKeyRef]: { size: 'foo' },
},
})

View File

@ -4,6 +4,6 @@
"version": "0.0.0",
"dependencies": {
"vue": "workspace:*",
"@vue/dts-built-test": "workspace:*"
"dts-built-test": "workspace:*"
}
}

View File

@ -5,6 +5,7 @@ import {
type Ref,
type ShallowRef,
type ToRefs,
type WritableComputedRef,
computed,
isRef,
proxyRefs,
@ -17,6 +18,7 @@ import {
toRefs,
toValue,
unref,
useTemplateRef,
} from 'vue'
import { type IsAny, type IsUnion, describe, expectType } from './utils'
@ -172,6 +174,67 @@ describe('ref with generic', <T extends { name: string }>() => {
expectType<string>(ss.value.name)
})
describe('allow getter and setter types to be unrelated', <T>() => {
const a = { b: ref(0) }
const c = ref(a)
c.value = a
const d = {} as T
const e = ref(d)
e.value = d
const f = ref(ref(0))
expectType<number>(f.value)
// @ts-expect-error
f.value = ref(1)
})
describe('correctly unwraps nested refs', () => {
const obj = {
n: 24,
ref: ref(24),
nestedRef: ref({ n: ref(0) }),
}
const a = ref(obj)
expectType<number>(a.value.n)
expectType<number>(a.value.ref)
expectType<number>(a.value.nestedRef.n)
const b = reactive({ a })
expectType<number>(b.a.n)
expectType<number>(b.a.ref)
expectType<number>(b.a.nestedRef.n)
})
// computed
describe('allow computed getter and setter types to be unrelated', () => {
const obj = ref({
name: 'foo',
})
const c = computed({
get() {
return JSON.stringify(obj.value)
},
set(val: typeof obj.value) {
obj.value = val
},
})
c.value = { name: 'bar' } // object
expectType<string>(c.value)
})
describe('Type safety for `WritableComputedRef` and `ComputedRef`', () => {
// @ts-expect-error
const writableComputed: WritableComputedRef<string> = computed(() => '')
// should allow
const immutableComputed: ComputedRef<string> = writableComputed
expectType<ComputedRef<string>>(immutableComputed)
})
// shallowRef
type Status = 'initial' | 'ready' | 'invalidating'
const shallowStatus = shallowRef<Status>('initial')
@ -452,3 +515,27 @@ describe('toRef <-> toValue', () => {
),
)
})
// unref
// #8747
declare const unref1: number | Ref<number> | ComputedRef<number>
expectType<number>(unref(unref1))
// #11356
declare const unref2:
| MaybeRef<string>
| ShallowRef<string>
| ComputedRef<string>
| WritableComputedRef<string>
expectType<string>(unref(unref2))
// toValue
expectType<number>(toValue(unref1))
expectType<string>(toValue(unref2))
// useTemplateRef
const tRef = useTemplateRef('foo')
expectType<Readonly<ShallowRef<unknown>>>(tRef)
const tRef2 = useTemplateRef<HTMLElement>('bar')
expectType<Readonly<ShallowRef<HTMLElement | null>>>(tRef2)

View File

@ -5,6 +5,7 @@ import {
defineComponent,
defineEmits,
defineModel,
defineOptions,
defineProps,
defineSlots,
toRefs,
@ -42,7 +43,8 @@ describe('defineProps w/ generics', () => {
test()
})
describe('defineProps w/ type declaration + withDefaults', () => {
describe('defineProps w/ type declaration + withDefaults', <T extends
string>() => {
const res = withDefaults(
defineProps<{
number?: number
@ -55,6 +57,7 @@ describe('defineProps w/ type declaration + withDefaults', () => {
z?: string
bool?: boolean
boolAndUndefined: boolean | undefined
foo?: T
}>(),
{
number: 123,
@ -64,6 +67,7 @@ describe('defineProps w/ type declaration + withDefaults', () => {
genStr: () => '',
y: undefined,
z: 'string',
foo: '' as any,
},
)
@ -80,6 +84,7 @@ describe('defineProps w/ type declaration + withDefaults', () => {
expectType<string | undefined>(res.x)
expectType<string | undefined>(res.y)
expectType<string>(res.z)
expectType<T>(res.foo)
expectType<boolean>(res.bool)
expectType<boolean>(res.boolAndUndefined)
@ -137,6 +142,31 @@ describe('defineProps w/ object union + withDefaults', () => {
>(props)
})
describe('defineProps w/ generic discriminate union + withDefaults', () => {
interface B {
b?: string
}
interface S<T> extends B {
mode: 'single'
v: T
}
interface M<T> extends B {
mode: 'multiple'
v: T[]
}
type Props = S<string> | M<string>
const props = withDefaults(defineProps<Props>(), {
b: 'b',
})
if (props.mode === 'single') {
expectType<string>(props.v)
}
if (props.mode === 'multiple') {
expectType<string[]>(props.v)
}
})
describe('defineProps w/ generic type declaration + withDefaults', <T extends
number, TA extends {
a: string
@ -197,6 +227,19 @@ describe('withDefaults w/ boolean type', () => {
expectType<boolean | undefined>(res2.bool)
})
describe('withDefaults w/ defineProp type is different from the defaults type', () => {
const res1 = withDefaults(
defineProps<{
bool?: boolean
}>(),
{ bool: false, value: false },
)
expectType<boolean>(res1.bool)
// @ts-expect-error
res1.value
})
describe('defineProps w/ runtime declaration', () => {
// runtime declaration
const props = defineProps({
@ -384,6 +427,51 @@ describe('defineModel', () => {
defineModel<string>({ default: 123 })
// @ts-expect-error unknown props option
defineModel({ foo: 123 })
// unrelated getter and setter types
{
const modelVal = defineModel({
get(_: string[]): string {
return ''
},
set(_: number) {
return 1
},
})
expectType<string | undefined>(modelVal.value)
modelVal.value = 1
modelVal.value = undefined
// @ts-expect-error
modelVal.value = 'foo'
const [modelVal2] = modelVal
expectType<string | undefined>(modelVal2.value)
modelVal2.value = 1
modelVal2.value = undefined
// @ts-expect-error
modelVal.value = 'foo'
const count = defineModel('count', {
get(_: string[]): string {
return ''
},
set(_: number) {
return ''
},
})
expectType<string | undefined>(count.value)
count.value = 1
count.value = undefined
// @ts-expect-error
count.value = 'foo'
const [count2] = count
expectType<string | undefined>(count2.value)
count2.value = 1
count2.value = undefined
// @ts-expect-error
count2.value = 'foo'
}
})
describe('useModel', () => {
@ -501,3 +589,21 @@ describe('toRefs w/ type declaration', () => {
}>()
expectType<Ref<File | File[] | undefined>>(toRefs(props).file)
})
describe('defineOptions', () => {
defineOptions({
name: 'MyComponent',
inheritAttrs: true,
})
defineOptions({
// @ts-expect-error props should be defined via defineProps()
props: ['props'],
// @ts-expect-error emits should be defined via defineEmits()
emits: ['emits'],
// @ts-expect-error slots should be defined via defineSlots()
slots: { default: 'default' },
// @ts-expect-error expose should be defined via defineExpose()
expose: ['expose'],
})
})

View File

@ -121,3 +121,5 @@ expectType<JSX.Element>(
xmlns="http://www.w3.org/2000/svg"
/>,
)
// details
expectType<JSX.Element>(<details name="details" />)

View File

@ -1,5 +1,6 @@
import {
type ComputedRef,
type MaybeRef,
type Ref,
computed,
defineComponent,
@ -203,3 +204,10 @@ defineComponent({
expectType<{ foo: string }>(value)
})
}
{
const css: MaybeRef<string> = ''
watch(ref(css), value => {
expectType<string>(value)
})
}

8
packages-private/global.d.ts vendored Normal file
View File

@ -0,0 +1,8 @@
/// <reference types="vite/client" />
// Global compile-time constants
declare var __COMMIT__: string
declare module 'file-saver' {
export function saveAs(blob: any, name: any): void
}

View File

@ -1,4 +1,4 @@
<!DOCTYPE html>
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
@ -7,13 +7,16 @@
<link rel="icon" type="image/svg" href="/logo.svg" />
<title>Vue SFC Playground</title>
<script>
const savedPreferDark = localStorage.getItem('vue-sfc-playground-prefer-dark')
if (
savedPreferDark === 'true' ||
(!savedPreferDark && window.matchMedia('(prefers-color-scheme: dark)').matches)
) {
const savedPreferDark = localStorage.getItem(
'vue-sfc-playground-prefer-dark',
)
if (
savedPreferDark === 'true' ||
(!savedPreferDark &&
window.matchMedia('(prefers-color-scheme: dark)').matches)
) {
document.documentElement.classList.add('dark')
}
}
</script>
<script type="module" src="/src/main.ts"></script>
</head>

View File

@ -9,11 +9,11 @@
"serve": "vite preview"
},
"devDependencies": {
"@vitejs/plugin-vue": "^5.0.5",
"@vitejs/plugin-vue": "catalog:",
"vite": "catalog:"
},
"dependencies": {
"@vue/repl": "^4.3.1",
"@vue/repl": "^4.4.2",
"file-saver": "^2.0.5",
"jszip": "^3.10.1",
"vue": "workspace:*"

View File

Before

Width:  |  Height:  |  Size: 261 B

After

Width:  |  Height:  |  Size: 261 B

View File

@ -14,6 +14,12 @@ setVH()
const useSSRMode = ref(false)
const AUTO_SAVE_STORAGE_KEY = 'vue-sfc-playground-auto-save'
const initAutoSave: boolean = JSON.parse(
localStorage.getItem(AUTO_SAVE_STORAGE_KEY) ?? 'true',
)
const autoSave = ref(initAutoSave)
const { productionMode, vueVersion, importMap } = useVueImportMap({
runtimeDev: import.meta.env.PROD
? `${location.origin}/vue.runtime.esm-browser.js`
@ -54,7 +60,8 @@ const sfcOptions = computed(
template: {
isProd: productionMode.value,
compilerOptions: {
isCustomElement: (tag: string) => tag === 'mjx-container',
isCustomElement: (tag: string) =>
tag === 'mjx-container' || tag.startsWith('custom-'),
},
},
}),
@ -88,6 +95,11 @@ function toggleSSR() {
useSSRMode.value = !useSSRMode.value
}
function toggleAutoSave() {
autoSave.value = !autoSave.value
localStorage.setItem(AUTO_SAVE_STORAGE_KEY, String(autoSave.value))
}
function reloadPage() {
replRef.value?.reload()
}
@ -110,9 +122,11 @@ onMounted(() => {
:store="store"
:prod="productionMode"
:ssr="useSSRMode"
:autoSave="autoSave"
@toggle-theme="toggleTheme"
@toggle-prod="toggleProdMode"
@toggle-ssr="toggleSSR"
@toggle-autosave="toggleAutoSave"
@reload-page="reloadPage"
/>
<Repl
@ -122,6 +136,8 @@ onMounted(() => {
@keydown.ctrl.s.prevent
@keydown.meta.s.prevent
:ssr="useSSRMode"
:model-value="autoSave"
:editorOptions="{ autoSaveText: false }"
:store="store"
:showCompileOutput="true"
:autoResize="true"
@ -129,7 +145,13 @@ onMounted(() => {
:preview-options="{
customCode: {
importCode: `import { initCustomFormatter } from 'vue'`,
useCode: `initCustomFormatter()`,
useCode: `if (window.devtoolsFormatters) {
const index = window.devtoolsFormatters.findIndex((v) => v.__vue_custom_formatter)
window.devtoolsFormatters.splice(index, 1)
initCustomFormatter()
} else {
initCustomFormatter()
}`,
},
}"
/>

View File

@ -14,11 +14,13 @@ const props = defineProps<{
store: ReplStore
prod: boolean
ssr: boolean
autoSave: boolean
}>()
const emit = defineEmits([
'toggle-theme',
'toggle-ssr',
'toggle-prod',
'toggle-autosave',
'reload-page',
])
@ -80,7 +82,7 @@ function toggleDark() {
pkg="vue"
label="Vue Version"
>
<li>
<li :class="{ active: vueVersion === `@${currentCommit}` }">
<a @click="resetVueVersion">This Commit ({{ currentCommit }})</a>
</li>
<li>
@ -107,6 +109,14 @@ function toggleDark() {
>
<span>{{ ssr ? 'SSR ON' : 'SSR OFF' }}</span>
</button>
<button
title="Toggle editor auto save mode"
class="toggle-autosave"
:class="{ enabled: autoSave }"
@click="$emit('toggle-autosave')"
>
<span>{{ autoSave ? 'AutoSave ON' : 'AutoSave OFF' }}</span>
</button>
<button title="Toggle dark mode" class="toggle-dark" @click="toggleDark">
<Sun class="light" />
<Moon class="dark" />
@ -125,7 +135,7 @@ function toggleDark() {
<Download />
</button>
<a
href="https://github.com/vuejs/core/tree/main/packages/sfc-playground"
href="https://github.com/vuejs/core/tree/main/packages-private/sfc-playground"
target="_blank"
title="View on GitHub"
class="github"
@ -199,7 +209,8 @@ h1 img {
}
.toggle-prod span,
.toggle-ssr span {
.toggle-ssr span,
.toggle-autosave span {
font-size: 12px;
border-radius: 4px;
padding: 4px 6px;
@ -214,11 +225,13 @@ h1 img {
background: var(--purple);
}
.toggle-ssr span {
.toggle-ssr span,
.toggle-autosave span {
background-color: var(--btn-bg);
}
.toggle-ssr.enabled span {
.toggle-ssr.enabled span,
.toggle-autosave.enabled span {
color: #fff;
background-color: var(--green);
}

View File

@ -1,5 +1,6 @@
<script setup lang="ts">
import { onMounted, ref } from 'vue'
import Copy from './icons/Copy.vue'
const expanded = ref(false)
const versions = ref<string[]>()
@ -53,6 +54,12 @@ function setVersion(v: string) {
expanded.value = false
}
function copyVersion(v: string) {
window.navigator.clipboard.writeText(v).then(() => {
alert('Vue version has been copied to clipboard.')
})
}
onMounted(() => {
window.addEventListener('click', () => {
expanded.value = false
@ -74,8 +81,21 @@ onMounted(() => {
<ul class="versions" :class="{ expanded }">
<li v-if="!versions"><a>loading versions...</a></li>
<li v-for="ver of versions" :class="{ active: ver === version }">
<li
v-for="(ver, index) of versions"
class="versions-item"
:class="{
active: ver === version || (version === 'latest' && index === 0),
}"
>
<a @click="setVersion(ver)">v{{ ver }}</a>
<button
title="Copy Version"
class="version-copy"
@click="copyVersion(`v${ver}`)"
>
<Copy />
</button>
</li>
<div @click="expanded = false">
<slot />
@ -115,4 +135,17 @@ onMounted(() => {
.versions .active a {
color: var(--green);
}
.versions .versions-item {
display: flex;
justify-content: space-between;
}
.versions .versions-item .version-copy {
display: none;
}
.versions .versions-item:hover .version-copy {
display: block;
}
</style>

View File

@ -1,4 +1,4 @@
<!DOCTYPE html>
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />

View File

@ -11,7 +11,7 @@
"vue": "^3.4.0"
},
"devDependencies": {
"@vitejs/plugin-vue": "^5.0.5",
"vite": "^5.3.3"
"@vitejs/plugin-vue": "^5.1.4",
"vite": "^5.4.8"
}
}

View File

@ -0,0 +1,14 @@
<template>
<svg
xmlns="http://www.w3.org/2000/svg"
width="1.3em"
height="1.3em"
viewBox="0 0 24 24"
>
<path fill="currentColor" d="M8 7h11v14H8z" opacity=".3" />
<path
fill="currentColor"
d="M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12zm3 4H8c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h11c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2m0 16H8V7h11z"
/>
</svg>
</template>

View File

@ -34,7 +34,7 @@ function copyVuePlugin(): Plugin {
name: 'copy-vue',
generateBundle() {
const copyFile = (file: string) => {
const filePath = path.resolve(__dirname, file)
const filePath = path.resolve(__dirname, '../../packages', file)
const basename = path.basename(file)
if (!fs.existsSync(filePath)) {
throw new Error(
@ -49,11 +49,11 @@ function copyVuePlugin(): Plugin {
})
}
copyFile(`../vue/dist/vue.esm-browser.js`)
copyFile(`../vue/dist/vue.esm-browser.prod.js`)
copyFile(`../vue/dist/vue.runtime.esm-browser.js`)
copyFile(`../vue/dist/vue.runtime.esm-browser.prod.js`)
copyFile(`../server-renderer/dist/server-renderer.esm-browser.js`)
copyFile(`vue/dist/vue.esm-browser.js`)
copyFile(`vue/dist/vue.esm-browser.prod.js`)
copyFile(`vue/dist/vue.runtime.esm-browser.js`)
copyFile(`vue/dist/vue.runtime.esm-browser.prod.js`)
copyFile(`server-renderer/dist/server-renderer.esm-browser.js`)
},
}
}

View File

@ -0,0 +1,24 @@
<title>Vue Template Explorer</title>
<link
rel="stylesheet"
data-name="vs/editor/editor.main"
href="https://unpkg.com/monaco-editor@0.20.0/min/vs/editor/editor.main.css"
/>
<link rel="stylesheet" href="./style.css" />
<div id="header"></div>
<div id="source" class="editor"></div>
<div id="output" class="editor"></div>
<script src="https://unpkg.com/monaco-editor@0.20.0/min/vs/loader.js"></script>
<script>
require.config({
paths: {
vs: 'https://unpkg.com/monaco-editor@0.20.0/min/vs',
},
})
</script>
<script src="./dist/template-explorer.global.js"></script>
<script>
require(['vs/editor/editor.main'], init /* injected by build */)
</script>

View File

@ -0,0 +1,24 @@
<title>Vue Template Explorer</title>
<link
rel="stylesheet"
data-name="vs/editor/editor.main"
href="./node_modules/monaco-editor/min/vs/editor/editor.main.css"
/>
<link rel="stylesheet" href="./style.css" />
<div id="header"></div>
<div id="source" class="editor"></div>
<div id="output" class="editor"></div>
<script src="./node_modules/monaco-editor/min/vs/loader.js"></script>
<script>
require.config({
paths: {
vs: './node_modules/monaco-editor/min/vs',
},
})
</script>
<script src="./dist/template-explorer.global.js"></script>
<script>
require(['vs/editor/editor.main'], init /* injected by build */)
</script>

View File

@ -11,7 +11,7 @@
"enableNonBrowserBranches": true
},
"dependencies": {
"monaco-editor": "^0.50.0",
"source-map-js": "^1.2.0"
"monaco-editor": "^0.52.0",
"source-map-js": "^1.2.1"
}
}

View File

@ -0,0 +1,7 @@
{
"extends": "../tsconfig.json",
"compilerOptions": {
"isolatedDeclarations": false
},
"include": ["."]
}

View File

@ -0,0 +1,15 @@
<script setup>
import { ref } from 'vue'
const count = ref(0)
</script>
<template>
<button @click="count++">{{ count }}</button>
</template>
<style>
button {
color: red;
}
</style>

View File

@ -0,0 +1 @@
This package is used for debugging issues that are related to `@vitejs/plugin-vue`, or can only be reproduced in a Vite-based setup. It aims to be as close to production as possible so Vue packages are resolved to the dist files instead of source.

View File

@ -0,0 +1,2 @@
<script type="module" src="./main.ts"></script>
<div id="app"></div>

View File

@ -0,0 +1,6 @@
import { createApp } from 'vue'
import App from './App.vue'
const app = createApp(App)
app.mount('#app')

View File

@ -0,0 +1,15 @@
{
"name": "vite-debug",
"private": true,
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"serve": "vite preview"
},
"devDependencies": {
"@vitejs/plugin-vue": "catalog:",
"vite": "catalog:",
"vue": "workspace:*"
}
}

View File

@ -0,0 +1,7 @@
{
"compilerOptions": {
"module": "esnext",
"moduleResolution": "bundler"
},
"include": ["./*"]
}

View File

@ -0,0 +1,6 @@
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
export default defineConfig({
plugins: [vue()],
})

View File

@ -19,12 +19,12 @@ export function render(_ctx, _cache) {
}"
`;
exports[`compiler: codegen > CacheExpression w/ isVNode: true 1`] = `
exports[`compiler: codegen > CacheExpression w/ isVOnce: true 1`] = `
"
export function render(_ctx, _cache) {
return _cache[1] || (
_setBlockTracking(-1),
_cache[1] = foo,
(_cache[1] = foo).cacheIndex = 1,
_setBlockTracking(1),
_cache[1]
)
@ -54,7 +54,7 @@ return function render(_ctx, _cache) {
[foo + bar]: bar
}, [
_createElementVNode("p", { "some-key": "foo" })
], 16)
], 16 /* FULL_PROPS */)
}
}"
`;
@ -98,7 +98,7 @@ exports[`compiler: codegen > forNode 1`] = `
"
return function render(_ctx, _cache) {
with (_ctx) {
return (_openBlock(true), _createElementBlock(_Fragment, null, _renderList(), 1))
return (_openBlock(true), _createElementBlock(_Fragment, null, _renderList(), 1 /* TEXT */))
}
}"
`;

View File

@ -2,7 +2,7 @@
exports[`compiler: parse > Edge Cases > invalid html 1`] = `
{
"cached": 0,
"cached": [],
"children": [
{
"children": [
@ -86,7 +86,7 @@ exports[`compiler: parse > Edge Cases > invalid html 1`] = `
exports[`compiler: parse > Edge Cases > self closing multiple tag 1`] = `
{
"cached": 0,
"cached": [],
"children": [
{
"children": [],
@ -280,7 +280,7 @@ exports[`compiler: parse > Edge Cases > self closing multiple tag 1`] = `
exports[`compiler: parse > Edge Cases > valid html 1`] = `
{
"cached": 0,
"cached": [],
"children": [
{
"children": [
@ -498,7 +498,7 @@ exports[`compiler: parse > Edge Cases > valid html 1`] = `
exports[`compiler: parse > Errors > CDATA_IN_HTML_CONTENT > <template><![CDATA[cdata]]></template> 1`] = `
{
"cached": 0,
"cached": [],
"children": [
{
"children": [],
@ -550,7 +550,7 @@ exports[`compiler: parse > Errors > CDATA_IN_HTML_CONTENT > <template><![CDATA[c
exports[`compiler: parse > Errors > CDATA_IN_HTML_CONTENT > <template><svg><![CDATA[cdata]]></svg></template> 1`] = `
{
"cached": 0,
"cached": [],
"children": [
{
"children": [
@ -643,7 +643,7 @@ exports[`compiler: parse > Errors > CDATA_IN_HTML_CONTENT > <template><svg><![CD
exports[`compiler: parse > Errors > DUPLICATE_ATTRIBUTE > <template><div id="" id=""></div></template> 1`] = `
{
"cached": 0,
"cached": [],
"children": [
{
"children": [
@ -813,7 +813,7 @@ exports[`compiler: parse > Errors > DUPLICATE_ATTRIBUTE > <template><div id="" i
exports[`compiler: parse > Errors > EOF_BEFORE_TAG_NAME > <template>< 1`] = `
{
"cached": 0,
"cached": [],
"children": [
{
"children": [
@ -883,7 +883,7 @@ exports[`compiler: parse > Errors > EOF_BEFORE_TAG_NAME > <template>< 1`] = `
exports[`compiler: parse > Errors > EOF_BEFORE_TAG_NAME > <template></ 1`] = `
{
"cached": 0,
"cached": [],
"children": [
{
"children": [
@ -953,7 +953,7 @@ exports[`compiler: parse > Errors > EOF_BEFORE_TAG_NAME > <template></ 1`] = `
exports[`compiler: parse > Errors > EOF_IN_CDATA > <template><svg><![CDATA[ 1`] = `
{
"cached": 0,
"cached": [],
"children": [
{
"children": [
@ -1028,7 +1028,7 @@ exports[`compiler: parse > Errors > EOF_IN_CDATA > <template><svg><![CDATA[ 1`]
exports[`compiler: parse > Errors > EOF_IN_CDATA > <template><svg><![CDATA[cdata 1`] = `
{
"cached": 0,
"cached": [],
"children": [
{
"children": [
@ -1121,7 +1121,7 @@ exports[`compiler: parse > Errors > EOF_IN_CDATA > <template><svg><![CDATA[cdata
exports[`compiler: parse > Errors > EOF_IN_COMMENT > <template><!-- 1`] = `
{
"cached": 0,
"cached": [],
"children": [
{
"children": [],
@ -1173,7 +1173,7 @@ exports[`compiler: parse > Errors > EOF_IN_COMMENT > <template><!-- 1`] = `
exports[`compiler: parse > Errors > EOF_IN_COMMENT > <template><!--comment 1`] = `
{
"cached": 0,
"cached": [],
"children": [
{
"children": [
@ -1243,7 +1243,7 @@ exports[`compiler: parse > Errors > EOF_IN_COMMENT > <template><!--comment 1`] =
exports[`compiler: parse > Errors > EOF_IN_TAG > <div></div 1`] = `
{
"cached": 0,
"cached": [],
"children": [
{
"children": [],
@ -1295,7 +1295,7 @@ exports[`compiler: parse > Errors > EOF_IN_TAG > <div></div 1`] = `
exports[`compiler: parse > Errors > EOF_IN_TAG > <template><div 1`] = `
{
"cached": 0,
"cached": [],
"children": [
{
"children": [],
@ -1347,7 +1347,7 @@ exports[`compiler: parse > Errors > EOF_IN_TAG > <template><div 1`] = `
exports[`compiler: parse > Errors > EOF_IN_TAG > <template><div 1`] = `
{
"cached": 0,
"cached": [],
"children": [
{
"children": [],
@ -1399,7 +1399,7 @@ exports[`compiler: parse > Errors > EOF_IN_TAG > <template><div 1`] = `
exports[`compiler: parse > Errors > EOF_IN_TAG > <template><div id 1`] = `
{
"cached": 0,
"cached": [],
"children": [
{
"children": [],
@ -1451,7 +1451,7 @@ exports[`compiler: parse > Errors > EOF_IN_TAG > <template><div id 1`] = `
exports[`compiler: parse > Errors > EOF_IN_TAG > <template><div id = 1`] = `
{
"cached": 0,
"cached": [],
"children": [
{
"children": [],
@ -1503,7 +1503,7 @@ exports[`compiler: parse > Errors > EOF_IN_TAG > <template><div id = 1`] = `
exports[`compiler: parse > Errors > EOF_IN_TAG > <template><div id 1`] = `
{
"cached": 0,
"cached": [],
"children": [
{
"children": [],
@ -1555,7 +1555,7 @@ exports[`compiler: parse > Errors > EOF_IN_TAG > <template><div id 1`] = `
exports[`compiler: parse > Errors > EOF_IN_TAG > <template><div id="abc 1`] = `
{
"cached": 0,
"cached": [],
"children": [
{
"children": [],
@ -1607,7 +1607,7 @@ exports[`compiler: parse > Errors > EOF_IN_TAG > <template><div id="abc 1`] = `
exports[`compiler: parse > Errors > EOF_IN_TAG > <template><div id="abc" 1`] = `
{
"cached": 0,
"cached": [],
"children": [
{
"children": [],
@ -1659,7 +1659,7 @@ exports[`compiler: parse > Errors > EOF_IN_TAG > <template><div id="abc" 1`] = `
exports[`compiler: parse > Errors > EOF_IN_TAG > <template><div id="abc"/ 1`] = `
{
"cached": 0,
"cached": [],
"children": [
{
"children": [
@ -1729,7 +1729,7 @@ exports[`compiler: parse > Errors > EOF_IN_TAG > <template><div id="abc"/ 1`] =
exports[`compiler: parse > Errors > EOF_IN_TAG > <template><div id='abc 1`] = `
{
"cached": 0,
"cached": [],
"children": [
{
"children": [],
@ -1781,7 +1781,7 @@ exports[`compiler: parse > Errors > EOF_IN_TAG > <template><div id='abc 1`] = `
exports[`compiler: parse > Errors > EOF_IN_TAG > <template><div id='abc' 1`] = `
{
"cached": 0,
"cached": [],
"children": [
{
"children": [],
@ -1833,7 +1833,7 @@ exports[`compiler: parse > Errors > EOF_IN_TAG > <template><div id='abc' 1`] = `
exports[`compiler: parse > Errors > EOF_IN_TAG > <template><div id='abc'/ 1`] = `
{
"cached": 0,
"cached": [],
"children": [
{
"children": [
@ -1903,7 +1903,7 @@ exports[`compiler: parse > Errors > EOF_IN_TAG > <template><div id='abc'/ 1`] =
exports[`compiler: parse > Errors > EOF_IN_TAG > <template><div id=abc / 1`] = `
{
"cached": 0,
"cached": [],
"children": [
{
"children": [
@ -1973,7 +1973,7 @@ exports[`compiler: parse > Errors > EOF_IN_TAG > <template><div id=abc / 1`] = `
exports[`compiler: parse > Errors > EOF_IN_TAG > <template><div id=abc 1`] = `
{
"cached": 0,
"cached": [],
"children": [
{
"children": [],
@ -2025,7 +2025,7 @@ exports[`compiler: parse > Errors > EOF_IN_TAG > <template><div id=abc 1`] = `
exports[`compiler: parse > Errors > MISSING_ATTRIBUTE_VALUE > <template><div id= /></div></template> 1`] = `
{
"cached": 0,
"cached": [],
"children": [
{
"children": [
@ -2148,7 +2148,7 @@ exports[`compiler: parse > Errors > MISSING_ATTRIBUTE_VALUE > <template><div id=
exports[`compiler: parse > Errors > MISSING_ATTRIBUTE_VALUE > <template><div id= ></div></template> 1`] = `
{
"cached": 0,
"cached": [],
"children": [
{
"children": [
@ -2271,7 +2271,7 @@ exports[`compiler: parse > Errors > MISSING_ATTRIBUTE_VALUE > <template><div id=
exports[`compiler: parse > Errors > MISSING_ATTRIBUTE_VALUE > <template><div id=></div></template> 1`] = `
{
"cached": 0,
"cached": [],
"children": [
{
"children": [
@ -2394,7 +2394,7 @@ exports[`compiler: parse > Errors > MISSING_ATTRIBUTE_VALUE > <template><div id=
exports[`compiler: parse > Errors > MISSING_END_TAG_NAME > <template></></template> 1`] = `
{
"cached": 0,
"cached": [],
"children": [
{
"children": [],
@ -2446,7 +2446,7 @@ exports[`compiler: parse > Errors > MISSING_END_TAG_NAME > <template></></templa
exports[`compiler: parse > Errors > UNEXPECTED_CHARACTER_IN_ATTRIBUTE_NAME > <template><div a"bc=''></div></template> 1`] = `
{
"cached": 0,
"cached": [],
"children": [
{
"children": [
@ -2569,7 +2569,7 @@ exports[`compiler: parse > Errors > UNEXPECTED_CHARACTER_IN_ATTRIBUTE_NAME > <te
exports[`compiler: parse > Errors > UNEXPECTED_CHARACTER_IN_ATTRIBUTE_NAME > <template><div a'bc=''></div></template> 1`] = `
{
"cached": 0,
"cached": [],
"children": [
{
"children": [
@ -2692,7 +2692,7 @@ exports[`compiler: parse > Errors > UNEXPECTED_CHARACTER_IN_ATTRIBUTE_NAME > <te
exports[`compiler: parse > Errors > UNEXPECTED_CHARACTER_IN_ATTRIBUTE_NAME > <template><div a<bc=''></div></template> 1`] = `
{
"cached": 0,
"cached": [],
"children": [
{
"children": [
@ -2815,7 +2815,7 @@ exports[`compiler: parse > Errors > UNEXPECTED_CHARACTER_IN_ATTRIBUTE_NAME > <te
exports[`compiler: parse > Errors > UNEXPECTED_CHARACTER_IN_UNQUOTED_ATTRIBUTE_VALUE > <template><div foo=bar"></div></template> 1`] = `
{
"cached": 0,
"cached": [],
"children": [
{
"children": [
@ -2938,7 +2938,7 @@ exports[`compiler: parse > Errors > UNEXPECTED_CHARACTER_IN_UNQUOTED_ATTRIBUTE_V
exports[`compiler: parse > Errors > UNEXPECTED_CHARACTER_IN_UNQUOTED_ATTRIBUTE_VALUE > <template><div foo=bar'></div></template> 1`] = `
{
"cached": 0,
"cached": [],
"children": [
{
"children": [
@ -3061,7 +3061,7 @@ exports[`compiler: parse > Errors > UNEXPECTED_CHARACTER_IN_UNQUOTED_ATTRIBUTE_V
exports[`compiler: parse > Errors > UNEXPECTED_CHARACTER_IN_UNQUOTED_ATTRIBUTE_VALUE > <template><div foo=bar<div></div></template> 1`] = `
{
"cached": 0,
"cached": [],
"children": [
{
"children": [
@ -3184,7 +3184,7 @@ exports[`compiler: parse > Errors > UNEXPECTED_CHARACTER_IN_UNQUOTED_ATTRIBUTE_V
exports[`compiler: parse > Errors > UNEXPECTED_CHARACTER_IN_UNQUOTED_ATTRIBUTE_VALUE > <template><div foo=bar=baz></div></template> 1`] = `
{
"cached": 0,
"cached": [],
"children": [
{
"children": [
@ -3307,7 +3307,7 @@ exports[`compiler: parse > Errors > UNEXPECTED_CHARACTER_IN_UNQUOTED_ATTRIBUTE_V
exports[`compiler: parse > Errors > UNEXPECTED_CHARACTER_IN_UNQUOTED_ATTRIBUTE_VALUE > <template><div foo=bar\`></div></template> 1`] = `
{
"cached": 0,
"cached": [],
"children": [
{
"children": [
@ -3430,7 +3430,7 @@ exports[`compiler: parse > Errors > UNEXPECTED_CHARACTER_IN_UNQUOTED_ATTRIBUTE_V
exports[`compiler: parse > Errors > UNEXPECTED_EQUALS_SIGN_BEFORE_ATTRIBUTE_NAME > <template><div =></div></template> 1`] = `
{
"cached": 0,
"cached": [],
"children": [
{
"children": [
@ -3537,7 +3537,7 @@ exports[`compiler: parse > Errors > UNEXPECTED_EQUALS_SIGN_BEFORE_ATTRIBUTE_NAME
exports[`compiler: parse > Errors > UNEXPECTED_EQUALS_SIGN_BEFORE_ATTRIBUTE_NAME > <template><div =foo=bar></div></template> 1`] = `
{
"cached": 0,
"cached": [],
"children": [
{
"children": [
@ -3660,7 +3660,7 @@ exports[`compiler: parse > Errors > UNEXPECTED_EQUALS_SIGN_BEFORE_ATTRIBUTE_NAME
exports[`compiler: parse > Errors > UNEXPECTED_QUESTION_MARK_INSTEAD_OF_TAG_NAME > <template><?xml?></template> 1`] = `
{
"cached": 0,
"cached": [],
"children": [
{
"children": [],
@ -3712,7 +3712,7 @@ exports[`compiler: parse > Errors > UNEXPECTED_QUESTION_MARK_INSTEAD_OF_TAG_NAME
exports[`compiler: parse > Errors > UNEXPECTED_SOLIDUS_IN_TAG > <template><div a/b></div></template> 1`] = `
{
"cached": 0,
"cached": [],
"children": [
{
"children": [
@ -3850,7 +3850,7 @@ exports[`compiler: parse > Errors > UNEXPECTED_SOLIDUS_IN_TAG > <template><div a
exports[`compiler: parse > Errors > X_INVALID_END_TAG > <svg><![CDATA[</div>]]></svg> 1`] = `
{
"cached": 0,
"cached": [],
"children": [
{
"children": [
@ -3920,7 +3920,7 @@ exports[`compiler: parse > Errors > X_INVALID_END_TAG > <svg><![CDATA[</div>]]><
exports[`compiler: parse > Errors > X_INVALID_END_TAG > <svg><!--</div>--></svg> 1`] = `
{
"cached": 0,
"cached": [],
"children": [
{
"children": [
@ -3990,7 +3990,7 @@ exports[`compiler: parse > Errors > X_INVALID_END_TAG > <svg><!--</div>--></svg>
exports[`compiler: parse > Errors > X_INVALID_END_TAG > <template></div></div></template> 1`] = `
{
"cached": 0,
"cached": [],
"children": [
{
"children": [],
@ -4042,7 +4042,7 @@ exports[`compiler: parse > Errors > X_INVALID_END_TAG > <template></div></div></
exports[`compiler: parse > Errors > X_INVALID_END_TAG > <template></div></template> 1`] = `
{
"cached": 0,
"cached": [],
"children": [
{
"children": [],
@ -4094,7 +4094,7 @@ exports[`compiler: parse > Errors > X_INVALID_END_TAG > <template></div></templa
exports[`compiler: parse > Errors > X_INVALID_END_TAG > <template>{{'</div>'}}</template> 1`] = `
{
"cached": 0,
"cached": [],
"children": [
{
"children": [
@ -4182,7 +4182,7 @@ exports[`compiler: parse > Errors > X_INVALID_END_TAG > <template>{{'</div>'}}</
exports[`compiler: parse > Errors > X_INVALID_END_TAG > <template>a </ b</template> 1`] = `
{
"cached": 0,
"cached": [],
"children": [
{
"children": [
@ -4252,7 +4252,7 @@ exports[`compiler: parse > Errors > X_INVALID_END_TAG > <template>a </ b</templa
exports[`compiler: parse > Errors > X_INVALID_END_TAG > <textarea></div></textarea> 1`] = `
{
"cached": 0,
"cached": [],
"children": [
{
"children": [
@ -4322,7 +4322,7 @@ exports[`compiler: parse > Errors > X_INVALID_END_TAG > <textarea></div></textar
exports[`compiler: parse > Errors > X_MISSING_DYNAMIC_DIRECTIVE_ARGUMENT_END > <div v-foo:[sef fsef] /> 1`] = `
{
"cached": 0,
"cached": [],
"children": [
{
"children": [],
@ -4446,7 +4446,7 @@ exports[`compiler: parse > Errors > X_MISSING_DYNAMIC_DIRECTIVE_ARGUMENT_END > <
exports[`compiler: parse > Errors > X_MISSING_END_TAG > <template><div> 1`] = `
{
"cached": 0,
"cached": [],
"children": [
{
"children": [
@ -4521,7 +4521,7 @@ exports[`compiler: parse > Errors > X_MISSING_END_TAG > <template><div> 1`] = `
exports[`compiler: parse > Errors > X_MISSING_END_TAG > <template><div></template> 1`] = `
{
"cached": 0,
"cached": [],
"children": [
{
"children": [
@ -4596,7 +4596,7 @@ exports[`compiler: parse > Errors > X_MISSING_END_TAG > <template><div></templat
exports[`compiler: parse > Errors > X_MISSING_INTERPOLATION_END > <div>{{ foo</div> 1`] = `
{
"cached": 0,
"cached": [],
"children": [
{
"children": [
@ -4666,7 +4666,7 @@ exports[`compiler: parse > Errors > X_MISSING_INTERPOLATION_END > <div>{{ foo</d
exports[`compiler: parse > Errors > X_MISSING_INTERPOLATION_END > {{ 1`] = `
{
"cached": 0,
"cached": [],
"children": [
{
"content": "{{",
@ -4713,7 +4713,7 @@ exports[`compiler: parse > Errors > X_MISSING_INTERPOLATION_END > {{ 1`] = `
exports[`compiler: parse > Errors > X_MISSING_INTERPOLATION_END > {{ foo 1`] = `
{
"cached": 0,
"cached": [],
"children": [
{
"content": "{{ foo",
@ -4760,7 +4760,7 @@ exports[`compiler: parse > Errors > X_MISSING_INTERPOLATION_END > {{ foo 1`] = `
exports[`compiler: parse > Errors > X_MISSING_INTERPOLATION_END > {{}} 1`] = `
{
"cached": 0,
"cached": [],
"children": [
{
"content": {

View File

@ -1,37 +1,5 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`scopeId compiler support > should push scopeId for hoisted nodes 1`] = `
"import { createElementVNode as _createElementVNode, toDisplayString as _toDisplayString, createTextVNode as _createTextVNode, openBlock as _openBlock, createElementBlock as _createElementBlock, pushScopeId as _pushScopeId, popScopeId as _popScopeId } from "vue"
const _withScopeId = n => (_pushScopeId("test"),n=n(),_popScopeId(),n)
const _hoisted_1 = /*#__PURE__*/ _withScopeId(() => /*#__PURE__*/_createElementVNode("div", null, "hello", -1 /* HOISTED */))
const _hoisted_2 = /*#__PURE__*/ _withScopeId(() => /*#__PURE__*/_createElementVNode("div", null, "world", -1 /* HOISTED */))
export function render(_ctx, _cache) {
return (_openBlock(), _createElementBlock("div", null, [
_hoisted_1,
_createTextVNode(_toDisplayString(_ctx.foo), 1 /* TEXT */),
_hoisted_2
]))
}"
`;
exports[`scopeId compiler support > should push typescript-compatible scopeId for hoisted nodes 1`] = `
"import { createElementVNode as _createElementVNode, toDisplayString as _toDisplayString, createTextVNode as _createTextVNode, openBlock as _openBlock, createElementBlock as _createElementBlock, pushScopeId as _pushScopeId, popScopeId as _popScopeId } from "vue"
const _withScopeId = (n: any) => (_pushScopeId("test"),n=n(),_popScopeId(),n)
const _hoisted_1 = /*#__PURE__*/ _withScopeId(() => /*#__PURE__*/_createElementVNode("div", null, "hello", -1 /* HOISTED */))
const _hoisted_2 = /*#__PURE__*/ _withScopeId(() => /*#__PURE__*/_createElementVNode("div", null, "world", -1 /* HOISTED */))
export function render(_ctx: any,_cache: any) {
return (_openBlock(), _createElementBlock("div", null, [
_hoisted_1,
_createTextVNode(_toDisplayString(_ctx.foo), 1 /* TEXT */),
_hoisted_2
]))
}"
`;
exports[`scopeId compiler support > should wrap default slot 1`] = `
"import { createElementVNode as _createElementVNode, resolveComponent as _resolveComponent, withCtx as _withCtx, openBlock as _openBlock, createBlock as _createBlock } from "vue"

View File

@ -47,7 +47,7 @@ function createRoot(options: Partial<RootNode> = {}): RootNode {
directives: [],
imports: [],
hoists: [],
cached: 0,
cached: [],
temps: 0,
codegenNode: createSimpleExpression(`null`, false),
loc: locStub,
@ -267,7 +267,7 @@ describe('compiler: codegen', () => {
disableTracking: true,
props: undefined,
children: createCallExpression(RENDER_LIST),
patchFlag: '1',
patchFlag: PatchFlags.TEXT,
dynamicProps: undefined,
directives: undefined,
loc: locStub,
@ -303,7 +303,7 @@ describe('compiler: codegen', () => {
disableTracking: false,
props: undefined,
children: createCallExpression(RENDER_LIST),
patchFlag: genFlagText(PatchFlags.STABLE_FRAGMENT),
patchFlag: PatchFlags.STABLE_FRAGMENT,
dynamicProps: undefined,
directives: undefined,
loc: locStub,
@ -364,7 +364,7 @@ describe('compiler: codegen', () => {
),
],
// flag
PatchFlags.FULL_PROPS + '',
PatchFlags.FULL_PROPS,
),
}),
)
@ -375,7 +375,7 @@ describe('compiler: codegen', () => {
[foo + bar]: bar
}, [
_${helperNameMap[CREATE_ELEMENT_VNODE]}("p", { "some-key": "foo" })
], ${PatchFlags.FULL_PROPS})`)
], ${genFlagText(PatchFlags.FULL_PROPS)})`)
expect(code).toMatchSnapshot()
})
@ -422,7 +422,7 @@ describe('compiler: codegen', () => {
test('CacheExpression', () => {
const { code } = generate(
createRoot({
cached: 1,
cached: [],
codegenNode: createCacheExpression(
1,
createSimpleExpression(`foo`, false),
@ -437,10 +437,10 @@ describe('compiler: codegen', () => {
expect(code).toMatchSnapshot()
})
test('CacheExpression w/ isVNode: true', () => {
test('CacheExpression w/ isVOnce: true', () => {
const { code } = generate(
createRoot({
cached: 1,
cached: [],
codegenNode: createCacheExpression(
1,
createSimpleExpression(`foo`, false),
@ -456,7 +456,7 @@ describe('compiler: codegen', () => {
`
_cache[1] || (
_setBlockTracking(-1),
_cache[1] = foo,
(_cache[1] = foo).cacheIndex = 1,
_setBlockTracking(1),
_cache[1]
)
@ -666,11 +666,14 @@ describe('compiler: codegen', () => {
})
test('with patchFlag and no children/props', () => {
expect(genCode(createVNodeCall(null, `"div"`, undefined, undefined, '1')))
.toMatchInlineSnapshot(`
"return _createElementVNode("div", null, null, 1)
"
`)
expect(
genCode(
createVNodeCall(null, `"div"`, undefined, undefined, PatchFlags.TEXT),
),
).toMatchInlineSnapshot(`
"return _createElementVNode("div", null, null, 1 /* TEXT */)
"
`)
})
test('as block', () => {

View File

@ -1358,7 +1358,27 @@ describe('compiler: parse', () => {
name: 'on',
rawName: 'v-on.enter',
arg: undefined,
modifiers: ['enter'],
modifiers: [
{
constType: 3,
content: 'enter',
isStatic: true,
loc: {
end: {
column: 16,
line: 1,
offset: 15,
},
source: 'enter',
start: {
column: 11,
line: 1,
offset: 10,
},
},
type: 4,
},
],
exp: undefined,
loc: {
start: { offset: 5, line: 1, column: 6 },
@ -1377,7 +1397,46 @@ describe('compiler: parse', () => {
name: 'on',
rawName: 'v-on.enter.exact',
arg: undefined,
modifiers: ['enter', 'exact'],
modifiers: [
{
constType: 3,
content: 'enter',
isStatic: true,
loc: {
end: {
column: 16,
line: 1,
offset: 15,
},
source: 'enter',
start: {
column: 11,
line: 1,
offset: 10,
},
},
type: 4,
},
{
constType: 3,
content: 'exact',
isStatic: true,
loc: {
end: {
column: 22,
line: 1,
offset: 21,
},
source: 'exact',
start: {
column: 17,
line: 1,
offset: 16,
},
},
type: 4,
},
],
exp: undefined,
loc: {
start: { offset: 5, line: 1, column: 6 },
@ -1406,7 +1465,46 @@ describe('compiler: parse', () => {
source: 'click',
},
},
modifiers: ['enter', 'exact'],
modifiers: [
{
constType: 3,
content: 'enter',
isStatic: true,
loc: {
end: {
column: 22,
line: 1,
offset: 21,
},
source: 'enter',
start: {
column: 17,
line: 1,
offset: 16,
},
},
type: 4,
},
{
constType: 3,
content: 'exact',
isStatic: true,
loc: {
end: {
column: 28,
line: 1,
offset: 27,
},
source: 'exact',
start: {
column: 23,
line: 1,
offset: 22,
},
},
type: 4,
},
],
exp: undefined,
loc: {
start: { offset: 5, line: 1, column: 6 },
@ -1435,7 +1533,27 @@ describe('compiler: parse', () => {
source: '[a.b]',
},
},
modifiers: ['camel'],
modifiers: [
{
constType: 3,
content: 'camel',
isStatic: true,
loc: {
end: {
column: 22,
line: 1,
offset: 21,
},
source: 'camel',
start: {
column: 17,
line: 1,
offset: 16,
},
},
type: 4,
},
],
exp: undefined,
loc: {
start: { offset: 5, line: 1, column: 6 },
@ -1530,7 +1648,27 @@ describe('compiler: parse', () => {
source: 'a',
},
},
modifiers: ['prop'],
modifiers: [
{
constType: 0,
content: 'prop',
isStatic: false,
loc: {
end: {
column: 1,
line: 1,
offset: 0,
},
source: '',
start: {
column: 1,
line: 1,
offset: 0,
},
},
type: 4,
},
],
exp: {
type: NodeTypes.SIMPLE_EXPRESSION,
content: 'b',
@ -1569,7 +1707,27 @@ describe('compiler: parse', () => {
source: 'a',
},
},
modifiers: ['sync'],
modifiers: [
{
constType: 3,
content: 'sync',
isStatic: true,
loc: {
end: {
column: 13,
line: 1,
offset: 12,
},
source: 'sync',
start: {
column: 9,
line: 1,
offset: 8,
},
},
type: 4,
},
],
exp: {
type: NodeTypes.SIMPLE_EXPRESSION,
content: 'b',
@ -1649,7 +1807,27 @@ describe('compiler: parse', () => {
source: 'a',
},
},
modifiers: ['enter'],
modifiers: [
{
constType: 3,
content: 'enter',
isStatic: true,
loc: {
end: {
column: 14,
line: 1,
offset: 13,
},
source: 'enter',
start: {
column: 9,
line: 1,
offset: 8,
},
},
type: 4,
},
],
exp: {
type: NodeTypes.SIMPLE_EXPRESSION,
content: 'b',
@ -1841,6 +2019,21 @@ describe('compiler: parse', () => {
children: [{ type: NodeTypes.TEXT, content: `{{ number ` }],
},
])
const ast3 = baseParse(`<div v-pre><textarea>{{ foo </textarea></div>`, {
parseMode: 'html',
})
expect((ast3.children[0] as ElementNode).children).toMatchObject([
{
type: NodeTypes.ELEMENT,
children: [
{
type: NodeTypes.TEXT,
content: `{{ foo `,
},
],
},
])
})
test('self-closing v-pre', () => {
@ -2176,6 +2369,7 @@ describe('compiler: parse', () => {
test('should remove leading newline character immediately following the pre element start tag', () => {
const ast = parse(`<pre>\n foo bar </pre>`, {
isPreTag: tag => tag === 'pre',
isIgnoreNewlineTag: tag => tag === 'pre',
})
expect(ast.children).toHaveLength(1)
const preElement = ast.children[0] as ElementNode

View File

@ -1,7 +1,4 @@
import { baseCompile } from '../src/compile'
import { POP_SCOPE_ID, PUSH_SCOPE_ID } from '../src/runtimeHelpers'
import { PatchFlags } from '@vue/shared'
import { genFlagText } from './testUtils'
/**
* Ensure all slot functions are wrapped with _withCtx
@ -57,53 +54,4 @@ describe('scopeId compiler support', () => {
expect(code).toMatch(/name: i,\s+fn: _withCtx\(/)
expect(code).toMatchSnapshot()
})
test('should push scopeId for hoisted nodes', () => {
const { ast, code } = baseCompile(
`<div><div>hello</div>{{ foo }}<div>world</div></div>`,
{
mode: 'module',
scopeId: 'test',
hoistStatic: true,
},
)
expect(ast.helpers).toContain(PUSH_SCOPE_ID)
expect(ast.helpers).toContain(POP_SCOPE_ID)
expect(ast.hoists.length).toBe(2)
;[
`const _withScopeId = n => (_pushScopeId("test"),n=n(),_popScopeId(),n)`,
`const _hoisted_1 = /*#__PURE__*/ _withScopeId(() => /*#__PURE__*/_createElementVNode("div", null, "hello", ${genFlagText(
PatchFlags.HOISTED,
)}))`,
`const _hoisted_2 = /*#__PURE__*/ _withScopeId(() => /*#__PURE__*/_createElementVNode("div", null, "world", ${genFlagText(
PatchFlags.HOISTED,
)}))`,
].forEach(c => expect(code).toMatch(c))
expect(code).toMatchSnapshot()
})
test('should push typescript-compatible scopeId for hoisted nodes', () => {
const { ast, code } = baseCompile(
`<div><div>hello</div>{{ foo }}<div>world</div></div>`,
{
mode: 'module',
scopeId: 'test',
hoistStatic: true,
isTS: true,
},
)
expect(ast.helpers).toContain(PUSH_SCOPE_ID)
expect(ast.helpers).toContain(POP_SCOPE_ID)
expect(ast.hoists.length).toBe(2)
;[
`const _withScopeId = (n: any) => (_pushScopeId("test"),n=n(),_popScopeId(),n)`,
`const _hoisted_1 = /*#__PURE__*/ _withScopeId(() => /*#__PURE__*/_createElementVNode("div", null, "hello", ${genFlagText(
PatchFlags.HOISTED,
)}))`,
`const _hoisted_2 = /*#__PURE__*/ _withScopeId(() => /*#__PURE__*/_createElementVNode("div", null, "world", ${genFlagText(
PatchFlags.HOISTED,
)}))`,
].forEach(c => expect(code).toMatch(c))
expect(code).toMatchSnapshot()
})
})

View File

@ -3,6 +3,8 @@ import {
ElementTypes,
Namespaces,
NodeTypes,
type Property,
type SimpleExpressionNode,
type VNodeCall,
locStub,
} from '../src'
@ -22,7 +24,10 @@ const bracketsRE = /^\[|\]$/g
// e.g.
// - createObjectMatcher({ 'foo': '[bar]' }) matches { foo: bar }
// - createObjectMatcher({ '[foo]': 'bar' }) matches { [foo]: "bar" }
export function createObjectMatcher(obj: Record<string, any>) {
export function createObjectMatcher(obj: Record<string, any>): {
type: NodeTypes
properties: Partial<Property>[]
} {
return {
type: NodeTypes.JS_OBJECT_EXPRESSION,
properties: Object.keys(obj).map(key => ({
@ -31,7 +36,7 @@ export function createObjectMatcher(obj: Record<string, any>) {
type: NodeTypes.SIMPLE_EXPRESSION,
content: key.replace(bracketsRE, ''),
isStatic: !leadingBracketRE.test(key),
},
} as SimpleExpressionNode,
value: isString(obj[key])
? {
type: NodeTypes.SIMPLE_EXPRESSION,
@ -78,7 +83,7 @@ type Flags = PatchFlags | ShapeFlags
export function genFlagText(
flag: Flags | Flags[],
names: { [k: number]: string } = PatchFlagNames,
) {
): string {
if (isArray(flag)) {
let f = 0
flag.forEach(ff => {

Some files were not shown because too many files have changed in this diff Show More