mirror of https://github.com/vuejs/core.git
Merge branch 'main' into fix-async-component
This commit is contained in:
commit
34553345da
|
@ -1,18 +1,17 @@
|
|||
{
|
||||
$schema: 'https://docs.renovatebot.com/renovate-schema.json',
|
||||
extends: ['config:base', 'schedule:weekly', 'group:allNonMajor'],
|
||||
extends: ['config:recommended', 'schedule:weekly', 'group:allNonMajor'],
|
||||
labels: ['dependencies'],
|
||||
ignorePaths: ['**/__tests__/**'],
|
||||
rangeStrategy: 'bump',
|
||||
packageRules: [
|
||||
{
|
||||
depTypeList: ['peerDependencies'],
|
||||
matchDepTypes: ['peerDependencies'],
|
||||
enabled: false,
|
||||
},
|
||||
{
|
||||
groupName: 'test',
|
||||
matchPackageNames: ['vitest', 'jsdom', 'puppeteer'],
|
||||
matchPackagePrefixes: ['@vitest'],
|
||||
matchPackageNames: ['vitest', 'jsdom', 'puppeteer', '@vitest{/,}**'],
|
||||
},
|
||||
{
|
||||
groupName: 'playground',
|
||||
|
@ -23,18 +22,28 @@
|
|||
},
|
||||
{
|
||||
groupName: 'compiler',
|
||||
matchPackageNames: ['magic-string'],
|
||||
matchPackagePrefixes: ['@babel', 'postcss'],
|
||||
matchPackageNames: ['magic-string', '@babel{/,}**', 'postcss{/,}**'],
|
||||
},
|
||||
{
|
||||
groupName: 'build',
|
||||
matchPackageNames: ['vite', '@swc/core'],
|
||||
matchPackagePrefixes: ['rollup', 'esbuild', '@rollup', '@vitejs'],
|
||||
matchPackageNames: [
|
||||
'vite',
|
||||
'@swc/core',
|
||||
'rollup{/,}**',
|
||||
'esbuild{/,}**',
|
||||
'@rollup{/,}**',
|
||||
'@vitejs{/,}**',
|
||||
],
|
||||
},
|
||||
{
|
||||
groupName: 'lint',
|
||||
matchPackageNames: ['simple-git-hooks', 'lint-staged'],
|
||||
matchPackagePrefixes: ['typescript-eslint', 'eslint', 'prettier'],
|
||||
matchPackageNames: [
|
||||
'simple-git-hooks',
|
||||
'lint-staged',
|
||||
'typescript-eslint{/,}**',
|
||||
'eslint{/,}**',
|
||||
'prettier{/,}**',
|
||||
],
|
||||
},
|
||||
],
|
||||
ignoreDeps: [
|
||||
|
@ -58,5 +67,9 @@
|
|||
// pinned
|
||||
// only used in example for e2e tests
|
||||
'marked',
|
||||
|
||||
// pinned, 5.0+ has exports issues
|
||||
// https://github.com/vuejs/core/issues/11603
|
||||
'entities',
|
||||
],
|
||||
}
|
||||
|
|
|
@ -3,6 +3,8 @@ on:
|
|||
push:
|
||||
branches:
|
||||
- '**'
|
||||
tags:
|
||||
- '!**'
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
|
@ -12,3 +14,29 @@ jobs:
|
|||
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
|
||||
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
|
||||
run: pnpm build --withTypes
|
||||
|
||||
- name: Release
|
||||
run: pnpx pkg-pr-new publish --compact --pnpm './packages/*'
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
}
|
||||
})
|
||||
|
|
|
@ -18,6 +18,7 @@ env:
|
|||
|
||||
jobs:
|
||||
upload:
|
||||
if: github.repository == 'vuejs/core'
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
|
|
|
@ -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:
|
||||
|
@ -65,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
|
||||
|
|
|
@ -1,42 +0,0 @@
|
|||
# upload built packages as artifacts for faster ecosystem-ci
|
||||
name: upload-built-packages
|
||||
|
||||
on:
|
||||
workflow_run:
|
||||
workflows: ['ci']
|
||||
branches: [main, minor]
|
||||
types:
|
||||
- completed
|
||||
|
||||
jobs:
|
||||
build-and-upload:
|
||||
if: >
|
||||
github.repository == 'vuejs/core' &&
|
||||
github.event.workflow_run.event == 'push' &&
|
||||
github.event.workflow_run.conclusion == 'success'
|
||||
runs-on: ubuntu-latest
|
||||
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
|
||||
run: pnpm build --withTypes
|
||||
|
||||
- name: Upload
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: packages
|
||||
path: packages/*/dist/*
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"recommendations": ["vitest.explorer"]
|
||||
}
|
|
@ -13,5 +13,6 @@
|
|||
},
|
||||
"[json]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
}
|
||||
},
|
||||
"editor.formatOnSave": true
|
||||
}
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
https://vuejs.org/funding.json
|
381
CHANGELOG.md
381
CHANGELOG.md
|
@ -1,3 +1,382 @@
|
|||
## [3.5.13](https://github.com/vuejs/core/compare/v3.5.12...v3.5.13) (2024-11-15)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **compiler-core:** handle v-memo + v-for with functional key ([#12014](https://github.com/vuejs/core/issues/12014)) ([99009ee](https://github.com/vuejs/core/commit/99009eee0efc238392daba93792d478525b21afa)), closes [#12013](https://github.com/vuejs/core/issues/12013)
|
||||
* **compiler-dom:** properly stringify template string style ([#12392](https://github.com/vuejs/core/issues/12392)) ([2d78539](https://github.com/vuejs/core/commit/2d78539da35322aea5f821b3cf9b02d006abac72)), closes [#12391](https://github.com/vuejs/core/issues/12391)
|
||||
* **custom-element:** avoid triggering mutationObserver when relecting props ([352bc88](https://github.com/vuejs/core/commit/352bc88c1bd2fda09c61ab17ea1a5967ffcd7bc0)), closes [#12214](https://github.com/vuejs/core/issues/12214) [#12215](https://github.com/vuejs/core/issues/12215)
|
||||
* **deps:** update dependency postcss to ^8.4.48 ([#12356](https://github.com/vuejs/core/issues/12356)) ([b5ff930](https://github.com/vuejs/core/commit/b5ff930089985a58c3553977ef999cec2a6708a4))
|
||||
* **hydration:** the component vnode's el should be updated when a mismatch occurs. ([#12255](https://github.com/vuejs/core/issues/12255)) ([a20a4cb](https://github.com/vuejs/core/commit/a20a4cb36a3e717d1f8f259d0d59f133f508ff0a)), closes [#12253](https://github.com/vuejs/core/issues/12253)
|
||||
* **reactiivty:** avoid unnecessary watcher effect removal from inactive scope ([2193284](https://github.com/vuejs/core/commit/21932840eae72ffcd357a62ec596aaecc7ec224a)), closes [#5783](https://github.com/vuejs/core/issues/5783) [#5806](https://github.com/vuejs/core/issues/5806)
|
||||
* **reactivity:** release nested effects/scopes on effect scope stop ([#12373](https://github.com/vuejs/core/issues/12373)) ([bee2f5e](https://github.com/vuejs/core/commit/bee2f5ee62dc0cd04123b737779550726374dd0a)), closes [#12370](https://github.com/vuejs/core/issues/12370)
|
||||
* **runtime-dom:** set css vars before user onMounted hooks ([2d5c5e2](https://github.com/vuejs/core/commit/2d5c5e25e9b7a56e883674fb434135ac514429b5)), closes [#11533](https://github.com/vuejs/core/issues/11533)
|
||||
* **runtime-dom:** set css vars on update to handle child forcing reflow in onMount ([#11561](https://github.com/vuejs/core/issues/11561)) ([c4312f9](https://github.com/vuejs/core/commit/c4312f9c715c131a09e552ba46e9beb4b36d55e6))
|
||||
* **ssr:** avoid updating subtree of async component if it is resolved ([#12363](https://github.com/vuejs/core/issues/12363)) ([da7ad5e](https://github.com/vuejs/core/commit/da7ad5e3d24f3e108401188d909d27a4910da095)), closes [#12362](https://github.com/vuejs/core/issues/12362)
|
||||
* **ssr:** ensure v-text updates correctly with custom directives in SSR output ([#12311](https://github.com/vuejs/core/issues/12311)) ([1f75d4e](https://github.com/vuejs/core/commit/1f75d4e6dfe18121ebe443cd3e8105d54f727893)), closes [#12309](https://github.com/vuejs/core/issues/12309)
|
||||
* **ssr:** handle initial selected state for select with v-model + v-for option ([#12399](https://github.com/vuejs/core/issues/12399)) ([4f8d807](https://github.com/vuejs/core/commit/4f8d8078221ee52deed266677a227ad2a6d8dd22)), closes [#12395](https://github.com/vuejs/core/issues/12395)
|
||||
* **teleport:** handle deferred teleport update before mounted ([#12168](https://github.com/vuejs/core/issues/12168)) ([8bff142](https://github.com/vuejs/core/commit/8bff142f99b646e9dd15897ec75368fbf34f1534)), closes [#12161](https://github.com/vuejs/core/issues/12161)
|
||||
* **templateRef:** set ref on cached async component which wrapped in KeepAlive ([#12290](https://github.com/vuejs/core/issues/12290)) ([983eb50](https://github.com/vuejs/core/commit/983eb50a17eac76f1bba4394ad0316c62b72191d)), closes [#4999](https://github.com/vuejs/core/issues/4999) [#5004](https://github.com/vuejs/core/issues/5004)
|
||||
* **test:** update snapshot ([#12169](https://github.com/vuejs/core/issues/12169)) ([828d4a4](https://github.com/vuejs/core/commit/828d4a443919fa2aa4e2e92fbd03a5f04b258eea))
|
||||
* **Transition:** fix transition memory leak edge case ([#12182](https://github.com/vuejs/core/issues/12182)) ([660132d](https://github.com/vuejs/core/commit/660132df6c6a8c14bf75e593dc47d2fdada30322)), closes [#12181](https://github.com/vuejs/core/issues/12181)
|
||||
* **transition:** reflow before leave-active class after leave-from ([#12288](https://github.com/vuejs/core/issues/12288)) ([4b479db](https://github.com/vuejs/core/commit/4b479db61d233b054561402ae94ef08550073ea1)), closes [#2593](https://github.com/vuejs/core/issues/2593)
|
||||
* **types:** defineEmits w/ interface declaration ([#12343](https://github.com/vuejs/core/issues/12343)) ([1022eab](https://github.com/vuejs/core/commit/1022eabaa1aaf8436876f5ec5573cb1e4b3959a6)), closes [#8457](https://github.com/vuejs/core/issues/8457)
|
||||
* **v-once:** setting hasOnce to current block only when in v-once ([#12374](https://github.com/vuejs/core/issues/12374)) ([37300fc](https://github.com/vuejs/core/commit/37300fc26190a7299efddbf98800ffd96d5cad96)), closes [#12371](https://github.com/vuejs/core/issues/12371)
|
||||
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* **reactivity:** do not track inner key `__v_skip`` ([#11690](https://github.com/vuejs/core/issues/11690)) ([d637bd6](https://github.com/vuejs/core/commit/d637bd6c0164c2883e6eabd3c2f1f8c258dedfb1))
|
||||
* **runtime-core:** use feature flag for call to resolveMergedOptions ([#12163](https://github.com/vuejs/core/issues/12163)) ([1755ac0](https://github.com/vuejs/core/commit/1755ac0a108ba3486bd8397e56d3bdcd69196594))
|
||||
|
||||
|
||||
|
||||
## [3.5.12](https://github.com/vuejs/core/compare/v3.5.11...v3.5.12) (2024-10-11)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **compiler-dom:** avoid stringify option with null value ([#12096](https://github.com/vuejs/core/issues/12096)) ([f6d9926](https://github.com/vuejs/core/commit/f6d99262364b7444ebab8742158599e8cdd79eaa)), closes [#12093](https://github.com/vuejs/core/issues/12093)
|
||||
* **compiler-sfc:** do not skip TSInstantiationExpression when transforming props destructure ([#12064](https://github.com/vuejs/core/issues/12064)) ([d3ecde8](https://github.com/vuejs/core/commit/d3ecde8a696ff62c8d0ab067fd1d7ee0565b63c5))
|
||||
* **compiler-sfc:** use sass modern api if available and avoid deprecation warning ([#11992](https://github.com/vuejs/core/issues/11992)) ([4474c11](https://github.com/vuejs/core/commit/4474c113d1fb1c26298dd6794275d5b5c7cc4d93))
|
||||
* **compiler:** clone loc to `ifNode` ([#12131](https://github.com/vuejs/core/issues/12131)) ([cde2c06](https://github.com/vuejs/core/commit/cde2c0671b00d4f6111fcbd7aa76e45872f20b0c)), closes [vuejs/language-tools#4911](https://github.com/vuejs/language-tools/issues/4911)
|
||||
* **custom-element:** properly remove hyphenated attribute ([#12143](https://github.com/vuejs/core/issues/12143)) ([e16e9a7](https://github.com/vuejs/core/commit/e16e9a7341e7cfb3c443da4e5e5b06e8158712c3)), closes [#12139](https://github.com/vuejs/core/issues/12139)
|
||||
* **defineModel:** handle kebab-case model correctly ([#12063](https://github.com/vuejs/core/issues/12063)) ([c0418a3](https://github.com/vuejs/core/commit/c0418a3b8fa96a0b108ab71b7aab5d3388f90557)), closes [#12060](https://github.com/vuejs/core/issues/12060)
|
||||
* **deps:** update dependency monaco-editor to ^0.52.0 ([#12119](https://github.com/vuejs/core/issues/12119)) ([f7cbea2](https://github.com/vuejs/core/commit/f7cbea2111c7770a180b640f36f6a5d4d6abc698))
|
||||
* **hydration:** provide compat fallback for idle callback hydration strategy ([#11935](https://github.com/vuejs/core/issues/11935)) ([1ae545a](https://github.com/vuejs/core/commit/1ae545a3786abef983be1c969726489685569c92))
|
||||
* **reactivity:** trigger reactivity for Map key `undefined` ([#12055](https://github.com/vuejs/core/issues/12055)) ([7ad289e](https://github.com/vuejs/core/commit/7ad289e1e7fea654524008ff91e43a8b8a55ef22)), closes [#12054](https://github.com/vuejs/core/issues/12054)
|
||||
* **runtime-core:** allow symbol values for slot prop key ([#12069](https://github.com/vuejs/core/issues/12069)) ([d9d4d4e](https://github.com/vuejs/core/commit/d9d4d4e158cd51a9ddda249f29de8467f60b2792)), closes [#12068](https://github.com/vuejs/core/issues/12068)
|
||||
* **runtime-core:** fix required prop check false positive for kebab-case edge cases ([#12034](https://github.com/vuejs/core/issues/12034)) ([9da1ac1](https://github.com/vuejs/core/commit/9da1ac156552ac449754e1373aac7e349841becb)), closes [#12011](https://github.com/vuejs/core/issues/12011)
|
||||
* **runtime-dom:** prevent unnecessary updates in v-model checkbox when value is unchanged ([#12146](https://github.com/vuejs/core/issues/12146)) ([ea943af](https://github.com/vuejs/core/commit/ea943afe404c4ca4b729906c5e8daf7aa2ccde9b)), closes [#12144](https://github.com/vuejs/core/issues/12144)
|
||||
* **teleport:** handle disabled teleport with updateCssVars ([#12113](https://github.com/vuejs/core/issues/12113)) ([76a8223](https://github.com/vuejs/core/commit/76a8223199c148b79a5c0ea19e235164809760cd)), closes [#12112](https://github.com/vuejs/core/issues/12112)
|
||||
* **transition/ssr:** make transition appear work with Suspense in SSR ([#12047](https://github.com/vuejs/core/issues/12047)) ([f1a4f67](https://github.com/vuejs/core/commit/f1a4f67aedfe83e440c54222213f070774faa421)), closes [#12046](https://github.com/vuejs/core/issues/12046)
|
||||
* **types:** ensure `this.$props` type does not include `string` ([#12123](https://github.com/vuejs/core/issues/12123)) ([704173e](https://github.com/vuejs/core/commit/704173e24276706de672cca6c9507e4dd9651197)), closes [#12122](https://github.com/vuejs/core/issues/12122)
|
||||
* **types:** retain union type narrowing with defaults applied ([#12108](https://github.com/vuejs/core/issues/12108)) ([05685a9](https://github.com/vuejs/core/commit/05685a9d7c42d4cd37169b867833776b91154fed)), closes [#12106](https://github.com/vuejs/core/issues/12106)
|
||||
* **useId:** ensure useId consistency when using serverPrefetch ([#12128](https://github.com/vuejs/core/issues/12128)) ([b4d3534](https://github.com/vuejs/core/commit/b4d35349d8bc39aa15bd3f1094d230e5928b177c)), closes [#12102](https://github.com/vuejs/core/issues/12102)
|
||||
* **watch:** watchEffect clean-up with SSR ([#12097](https://github.com/vuejs/core/issues/12097)) ([b094c72](https://github.com/vuejs/core/commit/b094c72b3d40c52c7124f145a9db028509a11202)), closes [#11956](https://github.com/vuejs/core/issues/11956)
|
||||
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* **reactivity:** avoid unnecessary recursion in removeSub ([#12135](https://github.com/vuejs/core/issues/12135)) ([ec917cf](https://github.com/vuejs/core/commit/ec917cfdb9d0169cd0835d3a0e28244242657dc9))
|
||||
|
||||
|
||||
|
||||
## [3.5.11](https://github.com/vuejs/core/compare/v3.5.10...v3.5.11) (2024-10-03)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **compiler-sfc:** do not skip `TSSatisfiesExpression` when transforming props destructure ([#12062](https://github.com/vuejs/core/issues/12062)) ([2328b05](https://github.com/vuejs/core/commit/2328b051f4efa1f1394b7d4e73b7c3f76e430e7c)), closes [#12061](https://github.com/vuejs/core/issues/12061)
|
||||
* **reactivity:** prevent overwriting `next` property during batch processing ([#12075](https://github.com/vuejs/core/issues/12075)) ([d3f5e6e](https://github.com/vuejs/core/commit/d3f5e6e5319b4ffaa55ca9a2ea3d95d78e76fa58)), closes [#12072](https://github.com/vuejs/core/issues/12072)
|
||||
* **scheduler:** job ordering when the post queue is flushing ([#12090](https://github.com/vuejs/core/issues/12090)) ([577edca](https://github.com/vuejs/core/commit/577edca8e7795436efd710d1c289ea8ea2642b0e))
|
||||
* **types:** correctly infer `TypeProps` when it is `any` ([#12073](https://github.com/vuejs/core/issues/12073)) ([57315ab](https://github.com/vuejs/core/commit/57315ab9688c9741a271d1075bbd28cbe5f71e2f)), closes [#12058](https://github.com/vuejs/core/issues/12058)
|
||||
* **types:** should not intersect `PublicProps` with `Props` ([#12077](https://github.com/vuejs/core/issues/12077)) ([6f85894](https://github.com/vuejs/core/commit/6f8589437635706f825ccec51800effba1d2bf5f))
|
||||
* **types:** infer the first generic type of `Ref` correctly ([#12094](https://github.com/vuejs/core/issues/12094)) ([c97bb84](https://github.com/vuejs/core/commit/c97bb84d0b0a16b012f886b6498e924415ed63e5))
|
||||
|
||||
|
||||
|
||||
## [3.5.10](https://github.com/vuejs/core/compare/v3.5.9...v3.5.10) (2024-09-27)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **custom-element:** properly set kebab-case props on Vue custom elements ([ea3efa0](https://github.com/vuejs/core/commit/ea3efa09e008918c1d9ba7226833a8b1a7a57244)), closes [#12030](https://github.com/vuejs/core/issues/12030) [#12032](https://github.com/vuejs/core/issues/12032)
|
||||
* **reactivity:** fix nested batch edge case ([93c95dd](https://github.com/vuejs/core/commit/93c95dd4cd416503f43a98a1455f62658d22b0b2))
|
||||
* **reactivity:** only clear notified flags for computed in first batch iteration ([aa9ef23](https://github.com/vuejs/core/commit/aa9ef2386a0cd39a174e5a887ec2b1a3525034fc)), closes [#12045](https://github.com/vuejs/core/issues/12045)
|
||||
* **types/ref:** handle nested refs in UnwrapRef ([#12049](https://github.com/vuejs/core/issues/12049)) ([e2c19c2](https://github.com/vuejs/core/commit/e2c19c20cfee9788519a80c0e53e216b78505994)), closes [#12044](https://github.com/vuejs/core/issues/12044)
|
||||
|
||||
|
||||
|
||||
## [3.5.9](https://github.com/vuejs/core/compare/v3.5.8...v3.5.9) (2024-09-26)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **reactivity:** fix property dep removal regression ([6001e5c](https://github.com/vuejs/core/commit/6001e5c81a05c894586f9287fbd991677bdd0455)), closes [#12020](https://github.com/vuejs/core/issues/12020) [#12021](https://github.com/vuejs/core/issues/12021)
|
||||
* **reactivity:** fix recursive sync watcher on computed edge case ([10ff159](https://github.com/vuejs/core/commit/10ff15924053d9bd95ad706f78ce09e288213fcf)), closes [#12033](https://github.com/vuejs/core/issues/12033) [#12037](https://github.com/vuejs/core/issues/12037)
|
||||
* **runtime-core:** avoid rendering plain object as VNode ([#12038](https://github.com/vuejs/core/issues/12038)) ([cb34b28](https://github.com/vuejs/core/commit/cb34b28a4a9bf868be4785b001c526163eda342e)), closes [#12035](https://github.com/vuejs/core/issues/12035) [vitejs/vite-plugin-vue#353](https://github.com/vitejs/vite-plugin-vue/issues/353)
|
||||
* **runtime-core:** make useId() always return a string ([a177092](https://github.com/vuejs/core/commit/a177092754642af2f98c33a4feffe8f198c3c950))
|
||||
* **types:** correct type inference of union event names ([#12022](https://github.com/vuejs/core/issues/12022)) ([4da6881](https://github.com/vuejs/core/commit/4da688141d9e7c15b622c289deaa81b11845b2c7))
|
||||
* **vue:** properly cache runtime compilation ([#12019](https://github.com/vuejs/core/issues/12019)) ([fa0ba24](https://github.com/vuejs/core/commit/fa0ba24b3ace02d7ecab65e57c2bea89a2550dcb))
|
||||
|
||||
|
||||
|
||||
## [3.5.8](https://github.com/vuejs/core/compare/v3.5.7...v3.5.8) (2024-09-22)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **reactivity:** do not remove dep from depsMap when cleaning up deps of computed ([#11995](https://github.com/vuejs/core/issues/11995)) ([0267a58](https://github.com/vuejs/core/commit/0267a588017eee4951ac2a877fe1ccae84cad905))
|
||||
|
||||
|
||||
|
||||
## [3.5.7](https://github.com/vuejs/core/compare/v3.5.6...v3.5.7) (2024-09-20)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **compile-core:** fix v-model with newlines edge case ([#11960](https://github.com/vuejs/core/issues/11960)) ([6224288](https://github.com/vuejs/core/commit/62242886d705ece88dbcad45bb78072ecccad0ca)), closes [#8306](https://github.com/vuejs/core/issues/8306)
|
||||
* **compiler-sfc:** initialize scope with null prototype object ([#11963](https://github.com/vuejs/core/issues/11963)) ([215e154](https://github.com/vuejs/core/commit/215e15407294bf667261360218f975b88c99c2e5))
|
||||
* **hydration:** avoid observing non-Element node ([#11954](https://github.com/vuejs/core/issues/11954)) ([7257e6a](https://github.com/vuejs/core/commit/7257e6a34200409b3fc347d3bb807e11e2785974)), closes [#11952](https://github.com/vuejs/core/issues/11952)
|
||||
* **reactivity:** do not remove dep from depsMap when unsubbed by computed ([960706e](https://github.com/vuejs/core/commit/960706eebf73f08ebc9d5dd853a05def05e2c153))
|
||||
* **reactivity:** fix dev-only memory leak by updating dep.subsHead on sub removal ([5c8b76e](https://github.com/vuejs/core/commit/5c8b76ed6cfbbcee4cbaac0b72beab7291044e4f)), closes [#11956](https://github.com/vuejs/core/issues/11956)
|
||||
* **reactivity:** fix memory leak from dep instances of garbage collected objects ([235ea47](https://github.com/vuejs/core/commit/235ea4772ed2972914cf142da8b7ac1fb04f7585)), closes [#11979](https://github.com/vuejs/core/issues/11979) [#11971](https://github.com/vuejs/core/issues/11971)
|
||||
* **reactivity:** fix triggerRef call on ObjectRefImpl returned by toRef ([#11986](https://github.com/vuejs/core/issues/11986)) ([b030c8b](https://github.com/vuejs/core/commit/b030c8bc7327877efb98aa3d9a58eb287a6ff07a)), closes [#11982](https://github.com/vuejs/core/issues/11982)
|
||||
* **scheduler:** ensure recursive jobs can't be queued twice ([#11955](https://github.com/vuejs/core/issues/11955)) ([d18d6aa](https://github.com/vuejs/core/commit/d18d6aa1b20dc57a8103c51ec4d61e8e53ed936d))
|
||||
* **ssr:** don't render comments in TransitionGroup ([#11961](https://github.com/vuejs/core/issues/11961)) ([a2f6ede](https://github.com/vuejs/core/commit/a2f6edeb02faedbb673c4bc5c6a59d9a79a37d07)), closes [#11958](https://github.com/vuejs/core/issues/11958)
|
||||
* **transition:** respect `duration` setting even when it is `0` ([#11967](https://github.com/vuejs/core/issues/11967)) ([f927a4a](https://github.com/vuejs/core/commit/f927a4ae6f7c453f70ba89498ee0c737dc9866fd))
|
||||
* **types:** correct type inference of all-optional props ([#11644](https://github.com/vuejs/core/issues/11644)) ([9eca65e](https://github.com/vuejs/core/commit/9eca65ee9871d1ac878755afa9a3eb1b02030350)), closes [#11733](https://github.com/vuejs/core/issues/11733) [vuejs/language-tools#4704](https://github.com/vuejs/language-tools/issues/4704)
|
||||
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* **hydration:** avoid observer if element is in viewport ([#11639](https://github.com/vuejs/core/issues/11639)) ([e075dfa](https://github.com/vuejs/core/commit/e075dfad5c7649c6045e3711687ec888e7aa1a39))
|
||||
|
||||
|
||||
|
||||
## [3.5.6](https://github.com/vuejs/core/compare/v3.5.5...v3.5.6) (2024-09-16)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **compile-dom:** should be able to stringify mathML ([#11891](https://github.com/vuejs/core/issues/11891)) ([85c138c](https://github.com/vuejs/core/commit/85c138ced108268f7656b568dfd3036a1e0aae34))
|
||||
* **compiler-sfc:** preserve old behavior when using withDefaults with desutructure ([8492c3c](https://github.com/vuejs/core/commit/8492c3c49a922363d6c77ef192c133a8fbce6514)), closes [#11930](https://github.com/vuejs/core/issues/11930)
|
||||
* **reactivity:** avoid exponential perf cost and reduce call stack depth for deeply chained computeds ([#11944](https://github.com/vuejs/core/issues/11944)) ([c74bb8c](https://github.com/vuejs/core/commit/c74bb8c2dd9e82aaabb0a2a2b368e900929b513b)), closes [#11928](https://github.com/vuejs/core/issues/11928)
|
||||
* **reactivity:** rely on dirty check only when computed has deps ([#11931](https://github.com/vuejs/core/issues/11931)) ([aa5dafd](https://github.com/vuejs/core/commit/aa5dafd2b55d42d6a29316a3bc91aea85c676a0b)), closes [#11929](https://github.com/vuejs/core/issues/11929)
|
||||
* **watch:** `once` option should be ignored by watchEffect ([#11884](https://github.com/vuejs/core/issues/11884)) ([49fa673](https://github.com/vuejs/core/commit/49fa673493d93b77ddba2165ab6545bae84fd1ae))
|
||||
* **watch:** unwatch should be callable during SSR ([#11925](https://github.com/vuejs/core/issues/11925)) ([2d6adf7](https://github.com/vuejs/core/commit/2d6adf78a047eed091db277ffbd9df0822fb0bdd)), closes [#11924](https://github.com/vuejs/core/issues/11924)
|
||||
|
||||
|
||||
|
||||
## [3.5.5](https://github.com/vuejs/core/compare/v3.5.4...v3.5.5) (2024-09-13)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **compiler-core:** fix handling of delimiterOpen in VPre ([#11915](https://github.com/vuejs/core/issues/11915)) ([706d4ac](https://github.com/vuejs/core/commit/706d4ac1d0210b2d9134b3228280187fe02fc971)), closes [#11913](https://github.com/vuejs/core/issues/11913)
|
||||
* **compiler-dom:** fix stringify static edge for partially eligible chunks in cached parent ([1d99d61](https://github.com/vuejs/core/commit/1d99d61c1bd77f9ea6743f6214a82add8346a121)), closes [#11879](https://github.com/vuejs/core/issues/11879) [#11890](https://github.com/vuejs/core/issues/11890)
|
||||
* **compiler-dom:** should ignore leading newline in <textarea> per spec ([3c4bf76](https://github.com/vuejs/core/commit/3c4bf7627649ec1e3220f8c4e4163c20d2afb367))
|
||||
* **compiler-sfc:** nested css supports atrule and comment ([#11899](https://github.com/vuejs/core/issues/11899)) ([0e7bc71](https://github.com/vuejs/core/commit/0e7bc717e6640644f062957ec5031506f0dab215)), closes [#11896](https://github.com/vuejs/core/issues/11896)
|
||||
* **custom-element:** handle nested customElement mount w/ shadowRoot false ([#11861](https://github.com/vuejs/core/issues/11861)) ([f2d8019](https://github.com/vuejs/core/commit/f2d801918841e7673ff3f048d0d895592a2f7e23)), closes [#11851](https://github.com/vuejs/core/issues/11851) [#11871](https://github.com/vuejs/core/issues/11871)
|
||||
* **hmr:** reload async child wrapped in Suspense + KeepAlive ([#11907](https://github.com/vuejs/core/issues/11907)) ([10a2c60](https://github.com/vuejs/core/commit/10a2c6053bd30d160d0214bb3566f540187e6874)), closes [#11868](https://github.com/vuejs/core/issues/11868)
|
||||
* **hydration:** fix mismatch of leading newline in `<textarea>` and `<pre>` ([a5f3c2e](https://github.com/vuejs/core/commit/a5f3c2eb4d2e7fae93ff93ce865b269f01cc825e)), closes [#11873](https://github.com/vuejs/core/issues/11873) [#11874](https://github.com/vuejs/core/issues/11874)
|
||||
* **reactivity:** properly clean up deps, fix memory leak ([8ea5d6d](https://github.com/vuejs/core/commit/8ea5d6d6981ab7febda0be43c3c92b18869c3a2a)), closes [#11901](https://github.com/vuejs/core/issues/11901)
|
||||
* **runtime-core:** properly update async component nested in KeepAlive ([#11917](https://github.com/vuejs/core/issues/11917)) ([7fe6c79](https://github.com/vuejs/core/commit/7fe6c795a1fc7ddcea5ad91a56141561192373ac)), closes [#11916](https://github.com/vuejs/core/issues/11916)
|
||||
* **TransitionGroup:** not warn unkeyed text children with whitespece preserve ([#11888](https://github.com/vuejs/core/issues/11888)) ([7571f20](https://github.com/vuejs/core/commit/7571f20bc3d1854377a146f41d211e05bb68cd47)), closes [#11885](https://github.com/vuejs/core/issues/11885)
|
||||
|
||||
|
||||
|
||||
## [3.5.4](https://github.com/vuejs/core/compare/v3.5.3...v3.5.4) (2024-09-10)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **compiler-sfc:** correct scoped injection for nesting selector ([#11854](https://github.com/vuejs/core/issues/11854)) ([b1de75e](https://github.com/vuejs/core/commit/b1de75ed04626b6423085dfde91fb0cb481a25e8)), closes [#10567](https://github.com/vuejs/core/issues/10567)
|
||||
* **reactivity:** fix markRaw error on already marked object ([#11864](https://github.com/vuejs/core/issues/11864)) ([67d6596](https://github.com/vuejs/core/commit/67d6596d40b1807b9cd8eb0d9282932ea77be3c0)), closes [#11862](https://github.com/vuejs/core/issues/11862)
|
||||
* Revert "fix: Revert "fix(reactivity): self-referencing computed should refresh"" ([e596378](https://github.com/vuejs/core/commit/e596378e0be728dad7d60938449f3fa557ca2ec9))
|
||||
* **runtime-core:** handle shallow reactive arrays in renderList correctly ([#11870](https://github.com/vuejs/core/issues/11870)) ([ced59ab](https://github.com/vuejs/core/commit/ced59ab8f2f2e89c13119bab3a0c25a1a1f1c3d6)), closes [#11869](https://github.com/vuejs/core/issues/11869)
|
||||
* **types:** correctly infer `TypeEmits` with both tuple and function syntax ([#11840](https://github.com/vuejs/core/issues/11840)) ([dad6738](https://github.com/vuejs/core/commit/dad673809929c084dcb8e42640eb7daa675d4ea4)), closes [#11836](https://github.com/vuejs/core/issues/11836)
|
||||
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* **reactivity:** trigger deps directly instead of storing in an array first ([#11695](https://github.com/vuejs/core/issues/11695)) ([f80d447](https://github.com/vuejs/core/commit/f80d447c17662556e9e3f99f6d199967f4c8cf3d))
|
||||
|
||||
|
||||
|
||||
## [3.5.3](https://github.com/vuejs/core/compare/v3.5.2...v3.5.3) (2024-09-06)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **hydration:** check __asyncHydrate presence for vue3-lazy-hydration compat ([#11825](https://github.com/vuejs/core/issues/11825)) ([8e6c337](https://github.com/vuejs/core/commit/8e6c3378676be05cea7f53664442acdfb86784f9)), closes [#11793](https://github.com/vuejs/core/issues/11793)
|
||||
* Revert "fix(reactivity): self-referencing computed should refresh" ([35c760f](https://github.com/vuejs/core/commit/35c760f82f749f7c6e3f9bfead8221ce498e892f))
|
||||
* **ssr:** respect app.config.warnHandler during ssr ([bf3d9a2](https://github.com/vuejs/core/commit/bf3d9a2af41659a743706306fc798b3d215df5af)), closes [#11830](https://github.com/vuejs/core/issues/11830)
|
||||
* **Transition:** handle KeepAlive child unmount in Transition out-in mode ([#11833](https://github.com/vuejs/core/issues/11833)) ([6b7901d](https://github.com/vuejs/core/commit/6b7901d28ed3a6a9242c666cc1b8e3c0b0b0fe62)), closes [#11775](https://github.com/vuejs/core/issues/11775)
|
||||
* **useId:** make generated IDs selector compatible ([babfb4c](https://github.com/vuejs/core/commit/babfb4cbcbf98601d76c1d7653eae8d250ce2710)), closes [#11828](https://github.com/vuejs/core/issues/11828)
|
||||
|
||||
|
||||
|
||||
## [3.5.2](https://github.com/vuejs/core/compare/v3.5.1...v3.5.2) (2024-09-05)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **reactivity:** make toRaw work on proxies created by proxyRef ([46c3ab1](https://github.com/vuejs/core/commit/46c3ab1d714024894fa1d33e495d5d35c7817d4d))
|
||||
* **reactivity:** pass oldValue to computed getter ([#11813](https://github.com/vuejs/core/issues/11813)) ([98864a7](https://github.com/vuejs/core/commit/98864a7ef5c8080c407166c8221488a4eacbbc81)), closes [#11812](https://github.com/vuejs/core/issues/11812)
|
||||
* **reactivity:** prevent endless recursion in computed getters ([#11797](https://github.com/vuejs/core/issues/11797)) ([716275d](https://github.com/vuejs/core/commit/716275d1b1d2383d8ef0306fcd94558d4d9170f2))
|
||||
* **reactivity:** self-referencing computed should refresh ([e84c4a6](https://github.com/vuejs/core/commit/e84c4a608e9dc96fb2a4a29d538bcc64f26103a2)), closes [/github.com/vuejs/core/pull/11797#issuecomment-2330738633](https://github.com//github.com/vuejs/core/pull/11797/issues/issuecomment-2330738633)
|
||||
* **scheduler:** prevent duplicate jobs being queued ([#11826](https://github.com/vuejs/core/issues/11826)) ([df56cc5](https://github.com/vuejs/core/commit/df56cc528793b1d6131a1e64095dd5cb95c56bee)), closes [#11712](https://github.com/vuejs/core/issues/11712) [#11807](https://github.com/vuejs/core/issues/11807)
|
||||
* **suspense:** avoid updating anchor if activeBranch has not been rendered to the actual container ([#11818](https://github.com/vuejs/core/issues/11818)) ([3c0d531](https://github.com/vuejs/core/commit/3c0d531fa7fe762bfe46fbe63f318adc95221795)), closes [#11806](https://github.com/vuejs/core/issues/11806)
|
||||
* **Transition:** handle KeepAlive child unmount in Transition out-in mode ([#11778](https://github.com/vuejs/core/issues/11778)) ([3116553](https://github.com/vuejs/core/commit/311655352931863dfcf520b8cf29cebc5b7e1e00)), closes [#11775](https://github.com/vuejs/core/issues/11775)
|
||||
* **types:** add HTMLDialogElement missing close event ([#11811](https://github.com/vuejs/core/issues/11811)) ([3634f7a](https://github.com/vuejs/core/commit/3634f7a4c1649ad2e7e969eb4512512868c61d01))
|
||||
* **types:** added name attribute support to details tag ([#11823](https://github.com/vuejs/core/issues/11823)) ([c74176e](https://github.com/vuejs/core/commit/c74176ec7b4d1d34159ce21d600c04b157ac5549)), closes [#11821](https://github.com/vuejs/core/issues/11821)
|
||||
* **types:** fix defineComponent props inference when setup() has explicit annotation ([fca20a3](https://github.com/vuejs/core/commit/fca20a39aa4a6f98c8f972bd435ebb7dc535648a)), closes [#11803](https://github.com/vuejs/core/issues/11803)
|
||||
* **useTemplateRef:** properly fix readonly warning in dev and ensure prod behavior consistency ([9b7797d](https://github.com/vuejs/core/commit/9b7797d0d1fc773e979e042673d5b9b3151c40fc)), closes [#11808](https://github.com/vuejs/core/issues/11808) [#11816](https://github.com/vuejs/core/issues/11816) [#11810](https://github.com/vuejs/core/issues/11810)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **compiler-core:** parse modifiers as expression to provide location data ([#11819](https://github.com/vuejs/core/issues/11819)) ([3f13203](https://github.com/vuejs/core/commit/3f13203564164eeb2945bdc0b9ef755c37477d75))
|
||||
|
||||
|
||||
|
||||
## [3.5.1](https://github.com/vuejs/core/compare/v3.5.0...v3.5.1) (2024-09-04)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **build:** improve built-in components treeshakability ([4eee630](https://github.com/vuejs/core/commit/4eee630b3122a10d0baf9b91358cfffa92d6fd81))
|
||||
* **reactivity:** handle non-array arguments in reactive `concat` method ([#11794](https://github.com/vuejs/core/issues/11794)) ([475977a](https://github.com/vuejs/core/commit/475977a6f76b77392610e0a3ec2b0e076d1e1d59)), closes [#11792](https://github.com/vuejs/core/issues/11792)
|
||||
* **Transition:** avoid applying transition hooks on comment vnode ([#11788](https://github.com/vuejs/core/issues/11788)) ([51912f8](https://github.com/vuejs/core/commit/51912f8a02e35f172f6d30ed7a2f3a92c1407cf9)), closes [#11782](https://github.com/vuejs/core/issues/11782)
|
||||
* **types:** avoid using intersection type in `Readonly<...>` to fix JSDoc emit ([#11799](https://github.com/vuejs/core/issues/11799)) ([7518bc1](https://github.com/vuejs/core/commit/7518bc19dc73ba46dcf1eef6e23f9e6e75552675))
|
||||
* **useTemplateRef:** fix readonly warning when useTemplateRef has same variable name as template ref ([bc63df0](https://github.com/vuejs/core/commit/bc63df01992fdbf0b6749ad234153725697ed896)), closes [#11795](https://github.com/vuejs/core/issues/11795) [#11802](https://github.com/vuejs/core/issues/11802) [#11804](https://github.com/vuejs/core/issues/11804)
|
||||
|
||||
|
||||
|
||||
# [3.5.0](https://github.com/vuejs/core/compare/v3.5.0-rc.1...v3.5.0) (2024-09-03)
|
||||
|
||||
## Aggregated Features List for 3.5 (alpha to stable)
|
||||
|
||||
### Reactivity
|
||||
|
||||
- **reactivity**: Refactor reactivity system to use version counting and doubly-linked list tracking ([#10397](https://github.com/vuejs/core/pull/10397)) ([05eb4e0](https://github.com/vuejs/core/commit/05eb4e0fefd585125dd60b7f8fe9c36928d921aa))
|
||||
- **reactivity**: Optimize array tracking ([#9511](https://github.com/vuejs/core/pull/9511)) ([70196a4](https://github.com/vuejs/core/commit/70196a40cc078f50fcc1110c38c06fbcc70b205e))
|
||||
- **compiler-sfc:** enable reactive props destructure by default ([d2dac0e](https://github.com/vuejs/core/commit/d2dac0e359c47d1ed0aa77eda488e76fd6466d2d))
|
||||
- **reactivity:** `onEffectCleanup` API ([2cc5615](https://github.com/vuejs/core/commit/2cc5615590de77126e8df46136de0240dbde5004)), closes [#10173](https://github.com/vuejs/core/issues/10173)
|
||||
- **reactivity:** add `failSilently` argument for `onScopeDispose` ([9a936aa](https://github.com/vuejs/core/commit/9a936aaec489c79433a32791ecf5ddb1739a62bd))
|
||||
- **reactivity/watch:** base `watch`, `getCurrentWatcher`, and `onWatcherCleanup` ([#9927](https://github.com/vuejs/core/issues/9927)) ([205e5b5](https://github.com/vuejs/core/commit/205e5b5e277243c3af2c937d9bd46cf671296b72))
|
||||
- **reactivity/watch:** add pause/resume for ReactiveEffect, EffectScope, and WatchHandle ([#9651](https://github.com/vuejs/core/issues/9651)) ([267093c](https://github.com/vuejs/core/commit/267093c31490050bfcf3ff2b30a2aefee2dad582))
|
||||
- **watch:** support passing number to `deep` option to control the watch depth ([#9572](https://github.com/vuejs/core/issues/9572)) ([22f7d96](https://github.com/vuejs/core/commit/22f7d96757956ebe0baafe52256aa327908cc51c))
|
||||
- **types:** export `MultiWatchSources` type ([#9563](https://github.com/vuejs/core/issues/9563)) ([998dca5](https://github.com/vuejs/core/commit/998dca59f140420280803233f41707580688562c))
|
||||
- **types:** allow computed getter and setter types to be unrelated ([#11472](https://github.com/vuejs/core/issues/11472)) ([a01675e](https://github.com/vuejs/core/commit/a01675ef8f99b5acd6832c53051f4415b18609f2)), closes [#7271](https://github.com/vuejs/core/issues/7271)
|
||||
|
||||
### SSR
|
||||
|
||||
- **runtime-core:** `useId()` and `app.config.idPrefix` ([#11404](https://github.com/vuejs/core/issues/11404)) ([73ef156](https://github.com/vuejs/core/commit/73ef1561f6905d69f968c094d0180c61824f1247))
|
||||
- **hydration:** lazy hydration strategies for async components ([#11458](https://github.com/vuejs/core/issues/11458)) ([d14a11c](https://github.com/vuejs/core/commit/d14a11c1cdcee88452f17ce97758743c863958f4))
|
||||
- **hydration:** support suppressing hydration mismatch via data-allow-mismatch ([94fb2b8](https://github.com/vuejs/core/commit/94fb2b8106a66bcca1a3f922a246a29fdd1274b1))
|
||||
|
||||
### Custom Element
|
||||
|
||||
- **custom-element:** `useHost()` helper ([775103a](https://github.com/vuejs/core/commit/775103af37df69d34c79f12c4c1776c47d07f0a0))
|
||||
- **custom-element:** `useShadowRoot()` helper ([5a1a89b](https://github.com/vuejs/core/commit/5a1a89bd6178cc2f84ba91da7d72aee4c6ec1282)), closes [#6113](https://github.com/vuejs/core/issues/6113) [#8195](https://github.com/vuejs/core/issues/8195)
|
||||
- **custom-element:** expose `this.$host` in Options API ([1ef8f46](https://github.com/vuejs/core/commit/1ef8f46af0cfdec2fed66376772409e0aa25ad50))
|
||||
- **custom-element:** inject child components styles to custom element shadow root ([#11517](https://github.com/vuejs/core/issues/11517)) ([56c76a8](https://github.com/vuejs/core/commit/56c76a8b05c45f782ed3a16ec77c6292b71a17f1)), closes [#4662](https://github.com/vuejs/core/issues/4662) [#7941](https://github.com/vuejs/core/issues/7941) [#7942](https://github.com/vuejs/core/issues/7942)
|
||||
- **custom-element:** support configurable app instance in defineCustomElement ([6758c3c](https://github.com/vuejs/core/commit/6758c3cd0427f97394d95168c655dae3b7fa62cd)), closes [#4356](https://github.com/vuejs/core/issues/4356) [#4635](https://github.com/vuejs/core/issues/4635)
|
||||
- **custom-element:** support css `:host` selector by applying css vars on host element ([#8830](https://github.com/vuejs/core/issues/8830)) ([03a9ea2](https://github.com/vuejs/core/commit/03a9ea2b88df0842a820e09f7445c4b9189e3fcb)), closes [#8826](https://github.com/vuejs/core/issues/8826)
|
||||
- **custom-element:** support emit with options ([e181bff](https://github.com/vuejs/core/commit/e181bff6dc39d5cef92000c10291243c7d6e4d08)), closes [#7605](https://github.com/vuejs/core/issues/7605)
|
||||
- **custom-element:** support expose on customElement ([#6256](https://github.com/vuejs/core/issues/6256)) ([af838c1](https://github.com/vuejs/core/commit/af838c1b5ec23552e52e64ffa7db0eb0246c3624)), closes [#5540](https://github.com/vuejs/core/issues/5540)
|
||||
- **custom-element:** support `nonce` option for injected style tags ([bb4a02a](https://github.com/vuejs/core/commit/bb4a02a70c30e739a3c705b3d96d09258d7d7ded)), closes [#6530](https://github.com/vuejs/core/issues/6530)
|
||||
- **custom-element:** support passing custom-element-specific options via 2nd argument of defineCustomElement ([60a88a2](https://github.com/vuejs/core/commit/60a88a2b129714186cf6ba66f30f31d733d0311e))
|
||||
- **custom-element:** support `shadowRoot: false` in `defineCustomElement()` ([37d2ce5](https://github.com/vuejs/core/commit/37d2ce5d8e0fac4a00064f02b05f91f69b2d5d5e)), closes [#4314](https://github.com/vuejs/core/issues/4314) [#4404](https://github.com/vuejs/core/issues/4404)
|
||||
|
||||
### Teleport
|
||||
|
||||
- **teleport:** support deferred Teleport ([#11387](https://github.com/vuejs/core/issues/11387)) ([59a3e88](https://github.com/vuejs/core/commit/59a3e88903b10ac2278170a44d5a03f24fef23ef)), closes [#2015](https://github.com/vuejs/core/issues/2015) [#11386](https://github.com/vuejs/core/issues/11386)
|
||||
- **teleport/transition:** support directly nesting Teleport inside Transition ([#6548](https://github.com/vuejs/core/issues/6548)) ([0e6e3c7](https://github.com/vuejs/core/commit/0e6e3c7eb0e5320b7c1818e025cb4a490fede9c0)), closes [#5836](https://github.com/vuejs/core/issues/5836)
|
||||
|
||||
### Misc
|
||||
|
||||
- **runtime-core:** `useTemplateRef()` ([3ba70e4](https://github.com/vuejs/core/commit/3ba70e49b5856c53611c314d4855d679a546a7df))
|
||||
- **runtime-core:** add `app.onUnmount()` for registering cleanup functions ([#4619](https://github.com/vuejs/core/issues/4619)) ([582a3a3](https://github.com/vuejs/core/commit/582a3a382b1adda565bac576b913a88d9e8d7a9e)), closes [#4516](https://github.com/vuejs/core/issues/4516)
|
||||
- **runtime-core:** add `app.config.throwUnhandledErrorInProduction` ([f476b7f](https://github.com/vuejs/core/commit/f476b7f030f2dd427ca655fcea36f4933a4b4da0)), closes [#7876](https://github.com/vuejs/core/issues/7876)
|
||||
- **runtime-dom:** Trusted Types compatibility ([#10844](https://github.com/vuejs/core/issues/10844)) ([6d4eb94](https://github.com/vuejs/core/commit/6d4eb94853ed1b2b1675bdd7d5ba9c75cc6daed5))
|
||||
- **compiler-core:** support `Symbol` global in template expressions ([#9069](https://github.com/vuejs/core/issues/9069)) ([a501a85](https://github.com/vuejs/core/commit/a501a85a7c910868e01a5c70a2abea4e9d9e87f3))
|
||||
- **types:** export more emit related types ([#11017](https://github.com/vuejs/core/issues/11017)) ([189573d](https://github.com/vuejs/core/commit/189573dcee2a16bd3ed36ff5589d43f535e5e733))
|
||||
* **types:** add loading prop to iframe ([#11767](https://github.com/vuejs/core/issues/11767)) ([d86fe0e](https://github.com/vuejs/core/commit/d86fe0ec002901dc359a0e85f3a421b4a8538d68))
|
||||
|
||||
### Internals
|
||||
|
||||
- **reactivity:** store value cache on CustomRefs impls ([#11539](https://github.com/vuejs/core/issues/11539)) ([e044b6e](https://github.com/vuejs/core/commit/e044b6e737efc9433d1d84590036b82280da6292))
|
||||
- **types:** provide internal options for directly using user types in language tools ([#10801](https://github.com/vuejs/core/issues/10801)) ([75c8cf6](https://github.com/vuejs/core/commit/75c8cf63a1ef30ac84f91282d66ad3f57c6612e9))
|
||||
- **types:** provide internal options for using refs type in language tools ([#11492](https://github.com/vuejs/core/issues/11492)) ([5ffd1a8](https://github.com/vuejs/core/commit/5ffd1a89455807d5069eb2c28eba0379641dca76))
|
||||
|
||||
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
* **compiler-sfc:** fix import usage check for kebab-case same name shorthand binding ([0f7c0e5](https://github.com/vuejs/core/commit/0f7c0e5dc0eedada7a5194db87fd0a7dbd1d3354)), closes [#11745](https://github.com/vuejs/core/issues/11745) [#11754](https://github.com/vuejs/core/issues/11754)
|
||||
* **cssVars:** correctly escape double quotes in SSR ([#11784](https://github.com/vuejs/core/issues/11784)) ([7b5b6e0](https://github.com/vuejs/core/commit/7b5b6e0275f35748dca6d7eb842f8ab2364c6b9a)), closes [#11779](https://github.com/vuejs/core/issues/11779)
|
||||
* **deps:** update dependency postcss to ^8.4.44 ([#11774](https://github.com/vuejs/core/issues/11774)) ([cb843e0](https://github.com/vuejs/core/commit/cb843e0be31f9e563ccfc30eca0c06f2a224b505))
|
||||
* **hydration:** escape css var name to avoid mismatch ([#11739](https://github.com/vuejs/core/issues/11739)) ([ca12e77](https://github.com/vuejs/core/commit/ca12e776bc53aaa31f2df6bb6edc6be1b2f10c37)), closes [#11735](https://github.com/vuejs/core/issues/11735)
|
||||
* **hydration:** handle text nodes with 0 during hydration ([#11772](https://github.com/vuejs/core/issues/11772)) ([c756da2](https://github.com/vuejs/core/commit/c756da24b2d8635cf52b4c7d3abf5bf938852cc5)), closes [#11771](https://github.com/vuejs/core/issues/11771)
|
||||
* **reactivity:** correctly handle method calls on user-extended arrays ([#11760](https://github.com/vuejs/core/issues/11760)) ([9817c80](https://github.com/vuejs/core/commit/9817c80187bec6a3344c74d65fac92262de0fcdd)), closes [#11759](https://github.com/vuejs/core/issues/11759)
|
||||
* **runtime-dom:** avoid unnecessary prop patch for checkbox ([#11657](https://github.com/vuejs/core/issues/11657)) ([c3ce9fe](https://github.com/vuejs/core/commit/c3ce9fe3d8fc27d864ce7148cd36da882cfc21ab)), closes [#11647](https://github.com/vuejs/core/issues/11647)
|
||||
* **runtime-dom:** prevent unnecessary DOM update from v-model ([#11656](https://github.com/vuejs/core/issues/11656)) ([b1be9bd](https://github.com/vuejs/core/commit/b1be9bd64f2c7c4286fecb25bad5d5edd49efce9)), closes [#11647](https://github.com/vuejs/core/issues/11647)
|
||||
* **server-renderer:** Fix call to serverPrefetch in server renderer with an async setup ([#10893](https://github.com/vuejs/core/issues/10893)) ([6039e25](https://github.com/vuejs/core/commit/6039e25e04a8c1db5821955f011d57f1615807ab))
|
||||
* **server-renderer:** render `className` during SSR ([#11722](https://github.com/vuejs/core/issues/11722)) ([52cdb0f](https://github.com/vuejs/core/commit/52cdb0f991dc154ae32a2900874d5dbc4e078565))
|
||||
* **types/defineModel:** allow getter and setter types to be unrelated ([#11699](https://github.com/vuejs/core/issues/11699)) ([fe07f70](https://github.com/vuejs/core/commit/fe07f7073617df358c2f8cbc3de433359e873c96)), closes [#11697](https://github.com/vuejs/core/issues/11697)
|
||||
|
||||
|
||||
|
||||
# [3.5.0-rc.1](https://github.com/vuejs/core/compare/v3.5.0-beta.3...v3.5.0-rc.1) (2024-08-29)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **compiler-sfc:** skip circular tsconfig project reference ([#11680](https://github.com/vuejs/core/issues/11680)) ([9c4c2e5](https://github.com/vuejs/core/commit/9c4c2e51b045218d0c5ca64b4fb58b17d5d580cc)), closes [#11382](https://github.com/vuejs/core/issues/11382)
|
||||
* **custom-element:** handle keys set on custom elements ([#11655](https://github.com/vuejs/core/issues/11655)) ([f1d1831](https://github.com/vuejs/core/commit/f1d1831f07fe52d5681a5ec9ec310572463abf26)), closes [#11641](https://github.com/vuejs/core/issues/11641)
|
||||
* **deps:** update dependency monaco-editor to ^0.51.0 ([#11713](https://github.com/vuejs/core/issues/11713)) ([434f8a9](https://github.com/vuejs/core/commit/434f8a97c77f68aeae050e9e4e1f54f63bc4bd26))
|
||||
* **keep-alive:** reset keep alive flag when the component is removed from include ([#11718](https://github.com/vuejs/core/issues/11718)) ([29c321b](https://github.com/vuejs/core/commit/29c321bfd33f9197244dec3d027077e63b2cdf2f)), closes [#11717](https://github.com/vuejs/core/issues/11717)
|
||||
* **reactivity:** avoid infinite recursion when mutating ref wrapped in reactive ([313e4bf](https://github.com/vuejs/core/commit/313e4bf55214ac1e334a99c329a3ba5daca4f156)), closes [#11696](https://github.com/vuejs/core/issues/11696)
|
||||
* **reactivity:** ensure watcher with once: true are properly removed from effect scope ([#11665](https://github.com/vuejs/core/issues/11665)) ([fbc0c42](https://github.com/vuejs/core/commit/fbc0c42bcf6dea5a6ae664223fa19d4375ca39f0))
|
||||
* **runtime-dom:** setting innerHTML when patching props should go through trusted types ([d875de5](https://github.com/vuejs/core/commit/d875de54e9e03e0768fe550aa4c4886a4baf3bd7))
|
||||
* **types:** GlobalDirective / GlobalComponents should not be records ([42e8df6](https://github.com/vuejs/core/commit/42e8df62030e7f2c287d9103f045e67b34a63e3b))
|
||||
|
||||
|
||||
|
||||
# [3.5.0-beta.3](https://github.com/vuejs/core/compare/v3.5.0-beta.2...v3.5.0-beta.3) (2024-08-20)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **reactivity:** extended methods respect reactive ([#11629](https://github.com/vuejs/core/issues/11629)) ([9de1d10](https://github.com/vuejs/core/commit/9de1d101f98bf6081f41038f6974826f190330a0)), closes [#11628](https://github.com/vuejs/core/issues/11628)
|
||||
* **runtime-core:** correct type inference for PascalCase emits ([#11579](https://github.com/vuejs/core/issues/11579)) ([d7d0371](https://github.com/vuejs/core/commit/d7d0371e74707ee601020f67de88e091cdae2673)), closes [vuejs/language-tools#4269](https://github.com/vuejs/language-tools/issues/4269)
|
||||
* **runtime-core:** ensure suspense content inherit scopeId ([#10652](https://github.com/vuejs/core/issues/10652)) ([ac2a410](https://github.com/vuejs/core/commit/ac2a410e46392db63ca4ed2db3c0fa71ebe1e855)), closes [#5148](https://github.com/vuejs/core/issues/5148)
|
||||
* **runtime-core:** pre jobs without an id should run first ([#7746](https://github.com/vuejs/core/issues/7746)) ([b332f80](https://github.com/vuejs/core/commit/b332f80f0edb018229a23b43b93bb402b6368a3c))
|
||||
* **ssr:** apply ssr props to the the fallback vnode-based branch in ssr ([#7247](https://github.com/vuejs/core/issues/7247)) ([98b83e8](https://github.com/vuejs/core/commit/98b83e86d16c635547a1e735e5fb675aea2f0f1b)), closes [#6123](https://github.com/vuejs/core/issues/6123)
|
||||
* **types/custom-element:** `defineCustomElement` with required props ([#11578](https://github.com/vuejs/core/issues/11578)) ([5e0f6d5](https://github.com/vuejs/core/commit/5e0f6d5f8fe7c4eb8f247357c3e2e281726f36db))
|
||||
* **types:** strip non-prop default values from return type of withDefaults ([#9998](https://github.com/vuejs/core/issues/9998)) ([44973bb](https://github.com/vuejs/core/commit/44973bb3e790db7d8aa7af4eda21c80cac73a8de)), closes [#9899](https://github.com/vuejs/core/issues/9899)
|
||||
* **watch:** handle errors in computed used as watch source ([#11626](https://github.com/vuejs/core/issues/11626)) ([8bcaad4](https://github.com/vuejs/core/commit/8bcaad4a32cf0f1f89e0259f6a53036620b7fe9f)), closes [#11624](https://github.com/vuejs/core/issues/11624)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **reactivity:** base `watch`, `getCurrentWatcher`, and `onWatcherCleanup` ([#9927](https://github.com/vuejs/core/issues/9927)) ([205e5b5](https://github.com/vuejs/core/commit/205e5b5e277243c3af2c937d9bd46cf671296b72))
|
||||
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* **runtime-core:** use `apply` to avoid spreading. ([#5985](https://github.com/vuejs/core/issues/5985)) ([bb6babc](https://github.com/vuejs/core/commit/bb6babca8f206615d4e246457cd54d21bb3bc5a4))
|
||||
|
||||
|
||||
|
||||
# [3.5.0-beta.2](https://github.com/vuejs/core/compare/v3.5.0-beta.1...v3.5.0-beta.2) (2024-08-15)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **build:** revert entities to 4.5 to avoid runtime resolution errors ([e9e0815](https://github.com/vuejs/core/commit/e9e08155bf8d00c3327ed7371330eb2ae467e560)), closes [#11603](https://github.com/vuejs/core/issues/11603)
|
||||
* **compiler-core:** use ast-based check for function expressions when possible ([5861229](https://github.com/vuejs/core/commit/58612294757480974e667652ede5bbcf72b1089d)), closes [#11615](https://github.com/vuejs/core/issues/11615)
|
||||
* **compiler-sfc:** fix prefixIdentifier default value ([3d6f015](https://github.com/vuejs/core/commit/3d6f01571b3fb61b32da599d0419eff4e3ebb231))
|
||||
* **compiler-sfc:** handle keyof operator with index object ([#11581](https://github.com/vuejs/core/issues/11581)) ([fe00815](https://github.com/vuejs/core/commit/fe008152c0612ff3ecc7ad88e7e66a06b1b2bc3f))
|
||||
* **custom-element:** keep instance.isCE for backwards compat ([e19fc27](https://github.com/vuejs/core/commit/e19fc270428b59456fee43224990138c4d6ccb2d))
|
||||
* **deps:** update dependency postcss to ^8.4.41 ([#11585](https://github.com/vuejs/core/issues/11585)) ([4c4e12a](https://github.com/vuejs/core/commit/4c4e12ae28d67d616924b0601e68adc551959971))
|
||||
* **keep-alive:** ensure include/exclude regexp work with global flag ([#11595](https://github.com/vuejs/core/issues/11595)) ([3653bc0](https://github.com/vuejs/core/commit/3653bc0f45d6fedf84e29b64ca52584359c383c0))
|
||||
* **reactivity:** ensure extended method arguments are not lost ([#11574](https://github.com/vuejs/core/issues/11574)) ([4085def](https://github.com/vuejs/core/commit/4085def1bae42d01ee3c22c731cc4a02096464ee)), closes [#11570](https://github.com/vuejs/core/issues/11570)
|
||||
* **reactivity:** sync watch should be executed correctly ([#11589](https://github.com/vuejs/core/issues/11589)) ([3bda3e8](https://github.com/vuejs/core/commit/3bda3e83fd9e2fbe451a1c79dae82ff6a7467683)), closes [#11577](https://github.com/vuejs/core/issues/11577)
|
||||
* **types/computed:** ensure type safety for `WritableComputedRef` ([#11608](https://github.com/vuejs/core/issues/11608)) ([5cf5a16](https://github.com/vuejs/core/commit/5cf5a1620d9a97382d386c277265d9dd051fe484))
|
||||
* **types:** add fallback stub for DOM types when DOM lib is absent ([#11598](https://github.com/vuejs/core/issues/11598)) ([fee6697](https://github.com/vuejs/core/commit/fee669764fbf475adce9e47a7a73b4937ab31ffc))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **deprecated:** remove deprecated parseExpressions option ([#11597](https://github.com/vuejs/core/issues/11597)) ([4e7d5db](https://github.com/vuejs/core/commit/4e7d5db4d276a5d4aaf3af7d43cfd28c171db307))
|
||||
|
||||
|
||||
|
||||
# [3.5.0-beta.1](https://github.com/vuejs/core/compare/v3.4.37...v3.5.0-beta.1) (2024-08-08)
|
||||
|
||||
|
||||
|
@ -119,7 +498,7 @@
|
|||
|
||||
## Previous Changelogs
|
||||
|
||||
### 3.4.x (2023-10-28 - 2024-08-08)
|
||||
### 3.4.x (2023-10-28 - 2024-08-15)
|
||||
|
||||
See [3.4 changelog](./changelogs/CHANGELOG-3.4.md)
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -1,3 +1,15 @@
|
|||
## [3.4.38](https://github.com/vuejs/core/compare/v3.4.37...v3.4.38) (2024-08-15)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **build:** revert entities to 4.5 to avoid runtime resolution errors ([f349af7](https://github.com/vuejs/core/commit/f349af7b65b9f8605d8b7bafcc06c25ab1f2daf0)), closes [#11603](https://github.com/vuejs/core/issues/11603)
|
||||
* **compiler-core:** use ast-based check for function expressions when possible ([236cac3](https://github.com/vuejs/core/commit/236cac3ff285890b8468dc827c463d87a91e516f)), closes [#11615](https://github.com/vuejs/core/issues/11615)
|
||||
* **compiler-sfc:** handle keyof operator with index object ([#11581](https://github.com/vuejs/core/issues/11581)) ([74d26db](https://github.com/vuejs/core/commit/74d26dbbe3cf2f70d1b772284eec6743ea946f6d))
|
||||
* **types:** add fallback stub for DOM types when DOM lib is absent ([#11598](https://github.com/vuejs/core/issues/11598)) ([4db0085](https://github.com/vuejs/core/commit/4db0085de316e1b773f474597915f9071d6ae6c6))
|
||||
|
||||
|
||||
|
||||
## [3.4.37](https://github.com/vuejs/core/compare/v3.4.36...v3.4.37) (2024-08-08)
|
||||
|
||||
|
||||
|
@ -12,6 +24,7 @@
|
|||
* Revert "fix(types/ref): correct type inference for nested refs ([#11536](https://github.com/vuejs/core/issues/11536))" ([3a56315](https://github.com/vuejs/core/commit/3a56315f94bc0e11cfbb288b65482ea8fc3a39b4))
|
||||
* **runtime-core:** fix warning for missing event handler ([#11489](https://github.com/vuejs/core/issues/11489)) ([e359ff0](https://github.com/vuejs/core/commit/e359ff0046286aee03fe31656c023677be457e07)), closes [#4803](https://github.com/vuejs/core/issues/4803) [#8268](https://github.com/vuejs/core/issues/8268)
|
||||
* **runtime-core:** prioritize using the provides from currentApp in nested createApp ([#11502](https://github.com/vuejs/core/issues/11502)) ([7e75de0](https://github.com/vuejs/core/commit/7e75de002f08076a02c9361a58fa1d0af1772964)), closes [#11488](https://github.com/vuejs/core/issues/11488)
|
||||
* Note: this change will break `inject` calls inside Pinia stores that expects to be able to inject provided values from the component using the store. This is expected because the usage is relying on previously incorrect behavior.
|
||||
* **runtime-dom:** apply css vars before mount ([#11538](https://github.com/vuejs/core/issues/11538)) ([fdc2a31](https://github.com/vuejs/core/commit/fdc2a31dbd4196d6432be16767a1bfdab1240d49)), closes [#11533](https://github.com/vuejs/core/issues/11533)
|
||||
* **ssr:** ensure content is valid when rendering normal slot ([#11491](https://github.com/vuejs/core/issues/11491)) ([6c90324](https://github.com/vuejs/core/commit/6c903248703e2413c6197b9ad4d535f31c8eac39)), closes [#11326](https://github.com/vuejs/core/issues/11326)
|
||||
* **types/ref:** correct type inference for nested refs ([#11536](https://github.com/vuejs/core/issues/11536)) ([536f623](https://github.com/vuejs/core/commit/536f62332c455ba82ef2979ba634b831f91928ba)), closes [#11532](https://github.com/vuejs/core/issues/11532) [#11537](https://github.com/vuejs/core/issues/11537)
|
||||
|
@ -728,17 +741,6 @@ Note that this is a type-only breaking change in a minor release, which adheres
|
|||
|
||||
|
||||
|
||||
## [3.3.13](https://github.com/vuejs/core/compare/v3.3.12...v3.3.13) (2023-12-19)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **compiler-core:** fix v-on with modifiers on inline expression of undefined ([#9866](https://github.com/vuejs/core/issues/9866)) ([bae79dd](https://github.com/vuejs/core/commit/bae79ddf8564a2da4a5365cfeb8d811990f42335)), closes [#9865](https://github.com/vuejs/core/issues/9865)
|
||||
* **runtime-dom:** cache event handlers by key/modifiers ([#9851](https://github.com/vuejs/core/issues/9851)) ([04d2c05](https://github.com/vuejs/core/commit/04d2c05054c26b02fbc1d84839b0ed5cd36455b6)), closes [#9849](https://github.com/vuejs/core/issues/9849)
|
||||
* **types:** extract properties from extended collections ([#9854](https://github.com/vuejs/core/issues/9854)) ([24b1c1d](https://github.com/vuejs/core/commit/24b1c1dd57fd55d998aa231a147500e010b10219)), closes [#9852](https://github.com/vuejs/core/issues/9852)
|
||||
|
||||
|
||||
|
||||
# [3.4.0-beta.3](https://github.com/vuejs/core/compare/v3.3.12...v3.4.0-beta.3) (2023-12-16)
|
||||
|
||||
|
||||
|
@ -751,19 +753,6 @@ Note that this is a type-only breaking change in a minor release, which adheres
|
|||
|
||||
|
||||
|
||||
## [3.3.12](https://github.com/vuejs/core/compare/v3.3.11...v3.3.12) (2023-12-16)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **hydration:** handle appear transition before patch props ([#9837](https://github.com/vuejs/core/issues/9837)) ([e70f4c4](https://github.com/vuejs/core/commit/e70f4c47c553b6e16d8fad70743271ca23802fe7)), closes [#9832](https://github.com/vuejs/core/issues/9832)
|
||||
* **sfc/cssVars:** fix loss of CSS v-bind variables when setting inline style with string value ([#9824](https://github.com/vuejs/core/issues/9824)) ([0a387df](https://github.com/vuejs/core/commit/0a387dfb1d04afb6eae4296b6da76dfdaca77af4)), closes [#9821](https://github.com/vuejs/core/issues/9821)
|
||||
* **ssr:** fix suspense hydration of fallback content ([#7188](https://github.com/vuejs/core/issues/7188)) ([60415b5](https://github.com/vuejs/core/commit/60415b5d67df55f1fd6b176615299c08640fa142))
|
||||
* **types:** add `xmlns:xlink` to `SVGAttributes` ([#9300](https://github.com/vuejs/core/issues/9300)) ([0d61b42](https://github.com/vuejs/core/commit/0d61b429ecf63591d31e09702058fa4c7132e1a7)), closes [#9299](https://github.com/vuejs/core/issues/9299)
|
||||
* **types:** fix `shallowRef` type error ([#9839](https://github.com/vuejs/core/issues/9839)) ([9a57158](https://github.com/vuejs/core/commit/9a571582b53220270e498d8712ea59312c0bef3a))
|
||||
* **types:** support for generic keyof slots ([#8374](https://github.com/vuejs/core/issues/8374)) ([213eba4](https://github.com/vuejs/core/commit/213eba479ce080efc1053fe636f6be4a4c889b44))
|
||||
|
||||
|
||||
|
||||
# [3.4.0-beta.2](https://github.com/vuejs/core/compare/v3.4.0-beta.1...v3.4.0-beta.2) (2023-12-14)
|
||||
|
||||
|
||||
|
@ -823,22 +812,6 @@ default.
|
|||
|
||||
|
||||
|
||||
## [3.3.11](https://github.com/vuejs/core/compare/v3.3.10...v3.3.11) (2023-12-08)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **custom-element:** correctly handle number type props in prod ([#8989](https://github.com/vuejs/core/issues/8989)) ([d74d364](https://github.com/vuejs/core/commit/d74d364d62db8e48881af6b5a75ce4fb5f36cc35))
|
||||
* **reactivity:** fix mutation on user proxy of reactive Array ([6ecbd5c](https://github.com/vuejs/core/commit/6ecbd5ce2a7f59314a8326a1d193874b87f4d8c8)), closes [#9742](https://github.com/vuejs/core/issues/9742) [#9751](https://github.com/vuejs/core/issues/9751) [#9750](https://github.com/vuejs/core/issues/9750)
|
||||
* **runtime-dom:** fix width and height prop check condition ([5b00286](https://github.com/vuejs/core/commit/5b002869c533220706f9788b496b8ca8d8e98609)), closes [#9762](https://github.com/vuejs/core/issues/9762)
|
||||
* **shared:** handle Map with symbol keys in toDisplayString ([#9731](https://github.com/vuejs/core/issues/9731)) ([364821d](https://github.com/vuejs/core/commit/364821d6bdb1775e2f55a69bcfb9f40f7acf1506)), closes [#9727](https://github.com/vuejs/core/issues/9727)
|
||||
* **shared:** handle more Symbol cases in toDisplayString ([983d45d](https://github.com/vuejs/core/commit/983d45d4f8eb766b5a16b7ea93b86d3c51618fa6))
|
||||
* **Suspense:** properly get anchor when mount fallback vnode ([#9770](https://github.com/vuejs/core/issues/9770)) ([b700328](https://github.com/vuejs/core/commit/b700328342e17dc16b19316c2e134a26107139d2)), closes [#9769](https://github.com/vuejs/core/issues/9769)
|
||||
* **types:** ref() return type should not be any when initial value is any ([#9768](https://github.com/vuejs/core/issues/9768)) ([cdac121](https://github.com/vuejs/core/commit/cdac12161ec27b45ded48854c3d749664b6d4a6d))
|
||||
* **watch:** should not fire pre watcher on child component unmount ([#7181](https://github.com/vuejs/core/issues/7181)) ([6784f0b](https://github.com/vuejs/core/commit/6784f0b1f8501746ea70d87d18ed63a62cf6b76d)), closes [#7030](https://github.com/vuejs/core/issues/7030)
|
||||
|
||||
|
||||
|
||||
# [3.4.0-alpha.4](https://github.com/vuejs/core/compare/v3.3.10...v3.4.0-alpha.4) (2023-12-04)
|
||||
|
||||
|
||||
|
@ -860,37 +833,6 @@ default.
|
|||
|
||||
|
||||
|
||||
## [3.3.10](https://github.com/vuejs/core/compare/v3.3.9...v3.3.10) (2023-12-04)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **app:** prevent template from being cached between apps with different options ([#9724](https://github.com/vuejs/core/issues/9724)) ([ec71585](https://github.com/vuejs/core/commit/ec715854ca12520b2afc9e9b3981cbae05ae5206)), closes [#9618](https://github.com/vuejs/core/issues/9618)
|
||||
* **compiler-sfc:** avoid passing forEach index to genMap ([f12db7f](https://github.com/vuejs/core/commit/f12db7fb564a534cef2e5805cc9f54afe5d72fbf))
|
||||
* **compiler-sfc:** deindent pug/jade templates ([6345197](https://github.com/vuejs/core/commit/634519720a21fb5a6871454e1cadad7053a568b8)), closes [#3231](https://github.com/vuejs/core/issues/3231) [#3842](https://github.com/vuejs/core/issues/3842) [#7723](https://github.com/vuejs/core/issues/7723)
|
||||
* **compiler-sfc:** fix :where and :is selector in scoped mode with multiple selectors ([#9735](https://github.com/vuejs/core/issues/9735)) ([c3e2c55](https://github.com/vuejs/core/commit/c3e2c556b532656b50b8ab5cd2d9eabc26622d63)), closes [#9707](https://github.com/vuejs/core/issues/9707)
|
||||
* **compiler-sfc:** generate more treeshaking friendly code ([#9507](https://github.com/vuejs/core/issues/9507)) ([8d74ca0](https://github.com/vuejs/core/commit/8d74ca0e6fa2738ca6854b7e879ff59419f948c7)), closes [#9500](https://github.com/vuejs/core/issues/9500)
|
||||
* **compiler-sfc:** support inferring generic types ([#8511](https://github.com/vuejs/core/issues/8511)) ([eb5e307](https://github.com/vuejs/core/commit/eb5e307c0be62002e62c4c800d0dfacb39b0d4ca)), closes [#8482](https://github.com/vuejs/core/issues/8482)
|
||||
* **compiler-sfc:** support resolving components from props ([#8785](https://github.com/vuejs/core/issues/8785)) ([7cbcee3](https://github.com/vuejs/core/commit/7cbcee3d831241a8bd3588ae92d3f27e3641e25f))
|
||||
* **compiler-sfc:** throw error when failing to load TS during type resolution ([#8883](https://github.com/vuejs/core/issues/8883)) ([4936d2e](https://github.com/vuejs/core/commit/4936d2e11a8d0ca3704bfe408548cb26bb3fd5e9))
|
||||
* **cssVars:** cssVar names should be double-escaped when generating code for ssr ([#8824](https://github.com/vuejs/core/issues/8824)) ([5199a12](https://github.com/vuejs/core/commit/5199a12f8855cd06f24bf355708b5a2134f63176)), closes [#7823](https://github.com/vuejs/core/issues/7823)
|
||||
* **deps:** update compiler to ^7.23.4 ([#9681](https://github.com/vuejs/core/issues/9681)) ([31f6ebc](https://github.com/vuejs/core/commit/31f6ebc4df84490ed29fb75e7bf4259200eb51f0))
|
||||
* **runtime-core:** Suspense get anchor properly in Transition ([#9309](https://github.com/vuejs/core/issues/9309)) ([65f3fe2](https://github.com/vuejs/core/commit/65f3fe273127a8b68e1222fbb306d28d85f01757)), closes [#8105](https://github.com/vuejs/core/issues/8105)
|
||||
* **runtime-dom:** set width/height with units as attribute ([#8781](https://github.com/vuejs/core/issues/8781)) ([bfc1838](https://github.com/vuejs/core/commit/bfc1838f31199de3f189198a3c234fa7bae91386))
|
||||
* **ssr:** avoid computed being accidentally cached before server render ([#9688](https://github.com/vuejs/core/issues/9688)) ([30d5d93](https://github.com/vuejs/core/commit/30d5d93a92b2154406ec04f8aca6b217fa01177c)), closes [#5300](https://github.com/vuejs/core/issues/5300)
|
||||
* **types:** expose emits as props in functional components ([#9234](https://github.com/vuejs/core/issues/9234)) ([887e54c](https://github.com/vuejs/core/commit/887e54c347ea9eac4c721b5e2288f054873d1d30))
|
||||
* **types:** fix reactive collection types ([#8960](https://github.com/vuejs/core/issues/8960)) ([ad27473](https://github.com/vuejs/core/commit/ad274737015c36906d76f3189203093fa3a2e4e7)), closes [#8904](https://github.com/vuejs/core/issues/8904)
|
||||
* **types:** improve return type withKeys and withModifiers ([#9734](https://github.com/vuejs/core/issues/9734)) ([43c3cfd](https://github.com/vuejs/core/commit/43c3cfdec5ae5d70fa2a21e857abc2d73f1a0d07))
|
||||
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* optimize on* prop check ([38aaa8c](https://github.com/vuejs/core/commit/38aaa8c88648c54fe2616ad9c0961288092fcb44))
|
||||
* **runtime-dom:** cache modifier wrapper functions ([da4a4fb](https://github.com/vuejs/core/commit/da4a4fb5e8eee3c6d31f24ebd79a9d0feca56cb2)), closes [#8882](https://github.com/vuejs/core/issues/8882)
|
||||
* **v-on:** constant handlers with modifiers should not be treated as dynamic ([4d94ebf](https://github.com/vuejs/core/commit/4d94ebfe75174b340d2b794e699cad1add3600a9))
|
||||
|
||||
|
||||
|
||||
# [3.4.0-alpha.3](https://github.com/vuejs/core/compare/v3.4.0-alpha.2...v3.4.0-alpha.3) (2023-11-28)
|
||||
|
||||
|
||||
|
@ -947,55 +889,6 @@ default.
|
|||
|
||||
|
||||
|
||||
## [3.3.9](https://github.com/vuejs/core/compare/v3.3.8...v3.3.9) (2023-11-25)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **compiler-core:** avoid rewriting scope variables in inline for loops ([#7245](https://github.com/vuejs/core/issues/7245)) ([a2d810e](https://github.com/vuejs/core/commit/a2d810eb40cef631f61991ca68b426ee9546aba0)), closes [#7238](https://github.com/vuejs/core/issues/7238)
|
||||
* **compiler-core:** fix `resolveParserPlugins` decorators check ([#9566](https://github.com/vuejs/core/issues/9566)) ([9d0eba9](https://github.com/vuejs/core/commit/9d0eba916f3bf6fb5c03222400edae1a2db7444f)), closes [#9560](https://github.com/vuejs/core/issues/9560)
|
||||
* **compiler-sfc:** consistently escape type-only prop names ([#8654](https://github.com/vuejs/core/issues/8654)) ([3e08d24](https://github.com/vuejs/core/commit/3e08d246dfd8523c54fb8e7a4a6fd5506ffb1bcc)), closes [#8635](https://github.com/vuejs/core/issues/8635) [#8910](https://github.com/vuejs/core/issues/8910) [vitejs/vite-plugin-vue#184](https://github.com/vitejs/vite-plugin-vue/issues/184)
|
||||
* **compiler-sfc:** malformed filename on windows using path.posix.join() ([#9478](https://github.com/vuejs/core/issues/9478)) ([f18a174](https://github.com/vuejs/core/commit/f18a174979626b3429db93c5d5b7ae5448917c70)), closes [#8671](https://github.com/vuejs/core/issues/8671) [#9583](https://github.com/vuejs/core/issues/9583) [#9446](https://github.com/vuejs/core/issues/9446) [#9473](https://github.com/vuejs/core/issues/9473)
|
||||
* **compiler-sfc:** support `:is` and `:where` selector in scoped css rewrite ([#8929](https://github.com/vuejs/core/issues/8929)) ([3227e50](https://github.com/vuejs/core/commit/3227e50b32105f8893f7dff2f29278c5b3a9f621))
|
||||
* **compiler-sfc:** support resolve extends interface for defineEmits ([#8470](https://github.com/vuejs/core/issues/8470)) ([9e1b74b](https://github.com/vuejs/core/commit/9e1b74bcd5fa4151f5d1bc02c69fbbfa4762f577)), closes [#8465](https://github.com/vuejs/core/issues/8465)
|
||||
* **hmr/transition:** fix kept-alive component inside transition disappearing after hmr ([#7126](https://github.com/vuejs/core/issues/7126)) ([d11e978](https://github.com/vuejs/core/commit/d11e978fc98dcc83526c167e603b8308f317f786)), closes [#7121](https://github.com/vuejs/core/issues/7121)
|
||||
* **hydration:** force hydration for v-bind with .prop modifier ([364f319](https://github.com/vuejs/core/commit/364f319d214226770d97c98d8fcada80c9e8dde3)), closes [#7490](https://github.com/vuejs/core/issues/7490)
|
||||
* **hydration:** properly hydrate indeterminate prop ([34b5a5d](https://github.com/vuejs/core/commit/34b5a5da4ae9c9faccac237acd7acc8e7e017571)), closes [#7476](https://github.com/vuejs/core/issues/7476)
|
||||
* **reactivity:** clear method on readonly collections should return undefined ([#7316](https://github.com/vuejs/core/issues/7316)) ([657476d](https://github.com/vuejs/core/commit/657476dcdb964be4fbb1277c215c073f3275728e))
|
||||
* **reactivity:** onCleanup also needs to be cleaned ([#8655](https://github.com/vuejs/core/issues/8655)) ([73fd810](https://github.com/vuejs/core/commit/73fd810eebdd383a2b4629f67736c4db1f428abd)), closes [#5151](https://github.com/vuejs/core/issues/5151) [#7695](https://github.com/vuejs/core/issues/7695)
|
||||
* **ssr:** hydration `__vnode` missing for devtools ([#9328](https://github.com/vuejs/core/issues/9328)) ([5156ac5](https://github.com/vuejs/core/commit/5156ac5b38cfa80d3db26f2c9bf40cb22a7521cb))
|
||||
* **types:** allow falsy value types in `StyleValue` ([#7954](https://github.com/vuejs/core/issues/7954)) ([17aa92b](https://github.com/vuejs/core/commit/17aa92b79b31d8bb8b5873ddc599420cb9806db8)), closes [#7955](https://github.com/vuejs/core/issues/7955)
|
||||
* **types:** defineCustomElement using defineComponent return type with emits ([#7937](https://github.com/vuejs/core/issues/7937)) ([5d932a8](https://github.com/vuejs/core/commit/5d932a8e6d14343c9d7fc7c2ecb58ac618b2f938)), closes [#7782](https://github.com/vuejs/core/issues/7782)
|
||||
* **types:** fix `unref` and `toValue` when input union type contains ComputedRef ([#8748](https://github.com/vuejs/core/issues/8748)) ([176d476](https://github.com/vuejs/core/commit/176d47671271b1abc21b1508e9a493c7efca6451)), closes [#8747](https://github.com/vuejs/core/issues/8747) [#8857](https://github.com/vuejs/core/issues/8857)
|
||||
* **types:** fix instance type when props type is incompatible with setup returned type ([#7338](https://github.com/vuejs/core/issues/7338)) ([0e1e8f9](https://github.com/vuejs/core/commit/0e1e8f919e5a74cdaadf9c80ee135088b25e7fa3)), closes [#5885](https://github.com/vuejs/core/issues/5885)
|
||||
* **types:** fix shallowRef return type with union value type ([#7853](https://github.com/vuejs/core/issues/7853)) ([7c44800](https://github.com/vuejs/core/commit/7c448000b0def910c2cfabfdf7ff20a3d6bc844f)), closes [#7852](https://github.com/vuejs/core/issues/7852)
|
||||
* **types:** more precise types for class bindings ([#8012](https://github.com/vuejs/core/issues/8012)) ([46e3374](https://github.com/vuejs/core/commit/46e33744c890bd49482c5e5c5cdea44e00ec84d5))
|
||||
* **types:** remove optional properties from defineProps return type ([#6421](https://github.com/vuejs/core/issues/6421)) ([94c049d](https://github.com/vuejs/core/commit/94c049d930d922069e38ea8700d7ff0970f71e61)), closes [#6420](https://github.com/vuejs/core/issues/6420)
|
||||
* **types:** return type of withDefaults should be readonly ([#8601](https://github.com/vuejs/core/issues/8601)) ([f15debc](https://github.com/vuejs/core/commit/f15debc01acb22d23f5acee97e6f02db88cef11a))
|
||||
* **types:** revert class type restrictions ([5d077c8](https://github.com/vuejs/core/commit/5d077c8754cc14f85d2d6d386df70cf8c0d93842)), closes [#8012](https://github.com/vuejs/core/issues/8012)
|
||||
* **types:** update jsx type definitions ([#8607](https://github.com/vuejs/core/issues/8607)) ([58e2a94](https://github.com/vuejs/core/commit/58e2a94871ae06a909c5f8bad07fb401193e6a38))
|
||||
* **types:** widen ClassValue type ([2424013](https://github.com/vuejs/core/commit/242401305944422d0c361b16101a4d18908927af))
|
||||
* **v-model:** avoid overwriting number input with same value ([#7004](https://github.com/vuejs/core/issues/7004)) ([40f4b77](https://github.com/vuejs/core/commit/40f4b77bb570868cb6e47791078767797e465989)), closes [#7003](https://github.com/vuejs/core/issues/7003)
|
||||
* **v-model:** unnecessary value binding error should apply to dynamic instead of static binding ([2859b65](https://github.com/vuejs/core/commit/2859b653c9a22460e60233cac10fe139e359b046)), closes [#3596](https://github.com/vuejs/core/issues/3596)
|
||||
|
||||
|
||||
|
||||
## [3.3.8](https://github.com/vuejs/core/compare/v3.3.7...v3.3.8) (2023-11-06)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **compile-sfc:** support `Error` type in `defineProps` ([#5955](https://github.com/vuejs/core/issues/5955)) ([a989345](https://github.com/vuejs/core/commit/a9893458ec519aae442e1b99e64e6d74685cd22c))
|
||||
* **compiler-core:** known global should be shadowed by local variables in expression rewrite ([#9492](https://github.com/vuejs/core/issues/9492)) ([a75d1c5](https://github.com/vuejs/core/commit/a75d1c5c6242e91a73cc5ba01e6da620dea0b3d9)), closes [#9482](https://github.com/vuejs/core/issues/9482)
|
||||
* **compiler-sfc:** fix dynamic directive arguments usage check for slots ([#9495](https://github.com/vuejs/core/issues/9495)) ([b39fa1f](https://github.com/vuejs/core/commit/b39fa1f8157647859331ce439c42ae016a49b415)), closes [#9493](https://github.com/vuejs/core/issues/9493)
|
||||
* **deps:** update dependency @vue/repl to ^2.6.2 ([#9536](https://github.com/vuejs/core/issues/9536)) ([5cef325](https://github.com/vuejs/core/commit/5cef325f41e3b38657c72fa1a38dedeee1c7a60a))
|
||||
* **deps:** update dependency @vue/repl to ^2.6.3 ([#9540](https://github.com/vuejs/core/issues/9540)) ([176d590](https://github.com/vuejs/core/commit/176d59058c9aecffe9da4d4311e98496684f06d4))
|
||||
* **hydration:** fix tagName access error on comment/text node hydration mismatch ([dd8a0cf](https://github.com/vuejs/core/commit/dd8a0cf5dcde13d2cbd899262a0e07f16e14e489)), closes [#9531](https://github.com/vuejs/core/issues/9531)
|
||||
* **types:** avoid exposing lru-cache types in generated dts ([462aeb3](https://github.com/vuejs/core/commit/462aeb3b600765e219ded2ee9a0ed1e74df61de0)), closes [#9521](https://github.com/vuejs/core/issues/9521)
|
||||
* **warn:** avoid warning on empty children with Suspense ([#3962](https://github.com/vuejs/core/issues/3962)) ([405f345](https://github.com/vuejs/core/commit/405f34587a63a5f1e3d147b9848219ea98acc22d))
|
||||
|
||||
|
||||
|
||||
# [3.4.0-alpha.1](https://github.com/vuejs/core/compare/v3.3.7...v3.4.0-alpha.1) (2023-10-28)
|
||||
|
||||
|
||||
|
|
|
@ -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']
|
||||
|
|
74
package.json
74
package.json
|
@ -1,14 +1,14 @@
|
|||
{
|
||||
"private": true,
|
||||
"version": "3.5.0-beta.1",
|
||||
"packageManager": "pnpm@9.7.0",
|
||||
"version": "3.5.13",
|
||||
"packageManager": "pnpm@9.15.2",
|
||||
"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,12 +17,15 @@
|
|||
"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-private/dts-built-test/tsconfig.json && tsc -p ./packages-private/dts-test/tsconfig.test.json",
|
||||
"test-coverage": "vitest -c vitest.unit.config.ts --coverage",
|
||||
"test-bench": "vitest bench",
|
||||
"test-coverage": "vitest run --project unit --coverage",
|
||||
"prebench": "node scripts/build.js -pf esm-browser reactivity",
|
||||
"prebench-compare": "node scripts/build.js -pf esm-browser reactivity",
|
||||
"bench": "vitest bench --project=unit --outputJson=temp/bench.json",
|
||||
"bench-compare": "vitest bench --project=unit --compare=temp/bench.json",
|
||||
"release": "node scripts/release.js",
|
||||
"changelog": "conventional-changelog -p angular -i CHANGELOG.md -s",
|
||||
"dev-esm": "node scripts/dev.js -if esm-bundler-runtime",
|
||||
|
@ -32,7 +35,7 @@
|
|||
"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",
|
||||
|
@ -61,54 +64,53 @@
|
|||
"devDependencies": {
|
||||
"@babel/parser": "catalog:",
|
||||
"@babel/types": "catalog:",
|
||||
"@rollup/plugin-alias": "^5.1.0",
|
||||
"@rollup/plugin-commonjs": "^26.0.1",
|
||||
"@rollup/plugin-alias": "^5.1.1",
|
||||
"@rollup/plugin-commonjs": "^28.0.2",
|
||||
"@rollup/plugin-json": "^6.1.0",
|
||||
"@rollup/plugin-node-resolve": "^15.2.3",
|
||||
"@rollup/plugin-node-resolve": "^16.0.0",
|
||||
"@rollup/plugin-replace": "5.0.4",
|
||||
"@swc/core": "^1.7.10",
|
||||
"@swc/core": "^1.10.3",
|
||||
"@types/hash-sum": "^1.0.2",
|
||||
"@types/node": "^20.14.15",
|
||||
"@types/node": "^22.10.2",
|
||||
"@types/semver": "^7.5.8",
|
||||
"@types/serve-handler": "^6.1.4",
|
||||
"@vitest/coverage-istanbul": "^2.0.5",
|
||||
"@vitest/coverage-v8": "^2.1.8",
|
||||
"@vue/consolidate": "1.0.0",
|
||||
"conventional-changelog-cli": "^5.0.0",
|
||||
"enquirer": "^2.4.1",
|
||||
"esbuild": "^0.23.0",
|
||||
"esbuild": "^0.24.2",
|
||||
"esbuild-plugin-polyfill-node": "^0.3.0",
|
||||
"eslint": "^9.9.0",
|
||||
"eslint-plugin-import-x": "^3.1.0",
|
||||
"eslint-plugin-vitest": "^0.5.4",
|
||||
"eslint": "^9.17.0",
|
||||
"eslint-plugin-import-x": "^4.6.1",
|
||||
"@vitest/eslint-plugin": "^1.1.20",
|
||||
"estree-walker": "catalog:",
|
||||
"jsdom": "^24.1.1",
|
||||
"lint-staged": "^15.2.8",
|
||||
"jsdom": "^25.0.1",
|
||||
"lint-staged": "^15.2.11",
|
||||
"lodash": "^4.17.21",
|
||||
"magic-string": "^0.30.11",
|
||||
"markdown-table": "^3.0.3",
|
||||
"magic-string": "^0.30.17",
|
||||
"markdown-table": "^3.0.4",
|
||||
"marked": "13.0.3",
|
||||
"npm-run-all2": "^6.2.2",
|
||||
"picocolors": "^1.0.1",
|
||||
"prettier": "^3.3.3",
|
||||
"npm-run-all2": "^7.0.2",
|
||||
"picocolors": "^1.1.1",
|
||||
"prettier": "^3.4.2",
|
||||
"pretty-bytes": "^6.1.1",
|
||||
"pug": "^3.0.3",
|
||||
"puppeteer": "~23.0.2",
|
||||
"puppeteer": "~23.3.0",
|
||||
"rimraf": "^6.0.1",
|
||||
"rollup": "^4.20.0",
|
||||
"rollup": "^4.29.1",
|
||||
"rollup-plugin-dts": "^6.1.1",
|
||||
"rollup-plugin-esbuild": "^6.1.1",
|
||||
"rollup-plugin-polyfill-node": "^0.13.0",
|
||||
"semver": "^7.6.3",
|
||||
"serve": "^14.2.3",
|
||||
"serve-handler": "^6.1.5",
|
||||
"serve": "^14.2.4",
|
||||
"serve-handler": "^6.1.6",
|
||||
"simple-git-hooks": "^2.11.1",
|
||||
"todomvc-app-css": "^2.4.3",
|
||||
"tslib": "^2.6.3",
|
||||
"tsx": "^4.17.0",
|
||||
"typescript": "~5.5.4",
|
||||
"typescript-eslint": "^8.0.1",
|
||||
"tslib": "^2.8.1",
|
||||
"typescript": "~5.6.2",
|
||||
"typescript-eslint": "^8.18.1",
|
||||
"vite": "catalog:",
|
||||
"vitest": "^2.0.5"
|
||||
"vitest": "^2.1.8"
|
||||
},
|
||||
"pnpm": {
|
||||
"peerDependencyRules": {
|
||||
|
|
|
@ -3,12 +3,17 @@ import { expectType } from './utils'
|
|||
|
||||
const app = createApp({})
|
||||
|
||||
app.directive<HTMLElement, string>('custom', {
|
||||
mounted(el, binding) {
|
||||
expectType<HTMLElement>(el)
|
||||
expectType<string>(binding.value)
|
||||
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)
|
||||
// @ts-expect-error not any
|
||||
expectType<number>(binding.value)
|
||||
},
|
||||
},
|
||||
})
|
||||
)
|
||||
|
|
|
@ -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 }) {
|
||||
|
@ -1775,6 +1822,15 @@ describe('__typeRefs backdoor, object syntax', () => {
|
|||
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],
|
||||
|
@ -2012,3 +2068,13 @@ expectString(instance.actionText)
|
|||
// public prop on $props should be optional
|
||||
// @ts-expect-error
|
||||
expectString(instance.$props.actionText)
|
||||
|
||||
// #12122
|
||||
defineComponent({
|
||||
props: { foo: String },
|
||||
render() {
|
||||
expectType<{ readonly foo?: string }>(this.$props)
|
||||
// @ts-expect-error
|
||||
expectType<string>(this.$props)
|
||||
},
|
||||
})
|
||||
|
|
|
@ -99,4 +99,37 @@ describe('defineCustomElement using defineComponent return type', () => {
|
|||
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
|
||||
})
|
||||
})
|
||||
|
|
|
@ -189,6 +189,24 @@ describe('allow getter and setter types to be unrelated', <T>() => {
|
|||
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({
|
||||
|
@ -209,6 +227,14 @@ describe('allow computed getter and setter types to be unrelated', () => {
|
|||
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')
|
||||
|
|
|
@ -227,6 +227,36 @@ 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('withDefaults w/ defineProp discriminate union type', () => {
|
||||
const props = withDefaults(
|
||||
defineProps<
|
||||
{ type: 'button'; buttonType?: 'submit' } | { type: 'link'; href: string }
|
||||
>(),
|
||||
{
|
||||
type: 'button',
|
||||
},
|
||||
)
|
||||
if (props.type === 'button') {
|
||||
expectType<'submit' | undefined>(props.buttonType)
|
||||
}
|
||||
if (props.type === 'link') {
|
||||
expectType<string>(props.href)
|
||||
}
|
||||
})
|
||||
|
||||
describe('defineProps w/ runtime declaration', () => {
|
||||
// runtime declaration
|
||||
const props = defineProps({
|
||||
|
@ -276,6 +306,14 @@ describe('defineEmits w/ type declaration', () => {
|
|||
emit2('baz')
|
||||
})
|
||||
|
||||
describe('defineEmits w/ interface declaration', () => {
|
||||
interface Emits {
|
||||
foo: [value: string]
|
||||
}
|
||||
const emit = defineEmits<Emits>()
|
||||
emit('foo', 'hi')
|
||||
})
|
||||
|
||||
describe('defineEmits w/ alt type declaration', () => {
|
||||
const emit = defineEmits<{
|
||||
foo: [id: string]
|
||||
|
@ -414,6 +452,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', () => {
|
||||
|
|
|
@ -121,3 +121,5 @@ expectType<JSX.Element>(
|
|||
xmlns="http://www.w3.org/2000/svg"
|
||||
/>,
|
||||
)
|
||||
// details
|
||||
expectType<JSX.Element>(<details name="details" />)
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -13,7 +13,7 @@
|
|||
"vite": "catalog:"
|
||||
},
|
||||
"dependencies": {
|
||||
"@vue/repl": "^4.3.1",
|
||||
"@vue/repl": "^4.4.2",
|
||||
"file-saver": "^2.0.5",
|
||||
"jszip": "^3.10.1",
|
||||
"vue": "workspace:*"
|
||||
|
|
|
@ -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`
|
||||
|
@ -89,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()
|
||||
}
|
||||
|
@ -111,9 +122,12 @@ onMounted(() => {
|
|||
:store="store"
|
||||
:prod="productionMode"
|
||||
:ssr="useSSRMode"
|
||||
:autoSave="autoSave"
|
||||
:theme="theme"
|
||||
@toggle-theme="toggleTheme"
|
||||
@toggle-prod="toggleProdMode"
|
||||
@toggle-ssr="toggleSSR"
|
||||
@toggle-autosave="toggleAutoSave"
|
||||
@reload-page="reloadPage"
|
||||
/>
|
||||
<Repl
|
||||
|
@ -123,6 +137,8 @@ onMounted(() => {
|
|||
@keydown.ctrl.s.prevent
|
||||
@keydown.meta.s.prevent
|
||||
:ssr="useSSRMode"
|
||||
:model-value="autoSave"
|
||||
:editorOptions="{ autoSaveText: false }"
|
||||
:store="store"
|
||||
:showCompileOutput="true"
|
||||
:autoResize="true"
|
||||
|
|
|
@ -14,11 +14,14 @@ const props = defineProps<{
|
|||
store: ReplStore
|
||||
prod: boolean
|
||||
ssr: boolean
|
||||
autoSave: boolean
|
||||
theme: 'dark' | 'light'
|
||||
}>()
|
||||
const emit = defineEmits([
|
||||
'toggle-theme',
|
||||
'toggle-ssr',
|
||||
'toggle-prod',
|
||||
'toggle-autosave',
|
||||
'reload-page',
|
||||
])
|
||||
|
||||
|
@ -43,6 +46,7 @@ function resetVueVersion() {
|
|||
|
||||
async function copyLink(e: MouseEvent) {
|
||||
if (e.metaKey) {
|
||||
resetVueVersion()
|
||||
// hidden logic for going to local debug from play.vuejs.org
|
||||
window.location.href = 'http://localhost:5173/' + window.location.hash
|
||||
return
|
||||
|
@ -107,7 +111,19 @@ function toggleDark() {
|
|||
>
|
||||
<span>{{ ssr ? 'SSR ON' : 'SSR OFF' }}</span>
|
||||
</button>
|
||||
<button title="Toggle dark mode" class="toggle-dark" @click="toggleDark">
|
||||
<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="`Switch to ${theme === 'dark' ? 'light' : 'dark'} theme`"
|
||||
class="toggle-dark"
|
||||
@click="toggleDark"
|
||||
>
|
||||
<Sun class="light" />
|
||||
<Moon class="dark" />
|
||||
</button>
|
||||
|
@ -199,7 +215,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 +231,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);
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
"vue": "^3.4.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vitejs/plugin-vue": "^5.1.2",
|
||||
"vite": "^5.4.0"
|
||||
"@vitejs/plugin-vue": "^5.2.1",
|
||||
"vite": "^6.0.6"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
"enableNonBrowserBranches": true
|
||||
},
|
||||
"dependencies": {
|
||||
"monaco-editor": "^0.50.0",
|
||||
"source-map-js": "^1.2.0"
|
||||
"monaco-editor": "^0.52.2",
|
||||
"source-map-js": "^1.2.1"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"extends": "../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"isolatedDeclarations": false
|
||||
},
|
||||
"include": ["."]
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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 => {
|
||||
|
|
|
@ -191,7 +191,7 @@ exports[`compiler: cacheStatic transform > prefixIdentifiers > hoist class with
|
|||
const { createElementVNode: _createElementVNode } = _Vue
|
||||
|
||||
const _hoisted_1 = {
|
||||
class: /*#__PURE__*/_normalizeClass({ foo: true })
|
||||
class: /*@__PURE__*/_normalizeClass({ foo: true })
|
||||
}
|
||||
|
||||
return function render(_ctx, _cache) {
|
||||
|
|
|
@ -1,5 +1,23 @@
|
|||
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||
|
||||
exports[`compiler: v-memo transform > element v-for key expression prefixing + v-memo 1`] = `
|
||||
"import { renderList as _renderList, Fragment as _Fragment, openBlock as _openBlock, createElementBlock as _createElementBlock, isMemoSame as _isMemoSame, withMemo as _withMemo } from "vue"
|
||||
|
||||
export function render(_ctx, _cache) {
|
||||
return (_openBlock(), _createElementBlock("div", null, [
|
||||
(_openBlock(true), _createElementBlock(_Fragment, null, _renderList(_ctx.tableData, (data, __, ___, _cached) => {
|
||||
const _memo = (_ctx.getLetter(data))
|
||||
if (_cached && _cached.key === _ctx.getId(data) && _isMemoSame(_cached, _memo)) return _cached
|
||||
const _item = (_openBlock(), _createElementBlock("span", {
|
||||
key: _ctx.getId(data)
|
||||
}))
|
||||
_item.memo = _memo
|
||||
return _item
|
||||
}, _cache, 0), 128 /* KEYED_FRAGMENT */))
|
||||
]))
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`compiler: v-memo transform > on component 1`] = `
|
||||
"import { resolveComponent as _resolveComponent, createVNode as _createVNode, withMemo as _withMemo, openBlock as _openBlock, createElementBlock as _createElementBlock } from "vue"
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ return function render(_ctx, _cache) {
|
|||
const { setBlockTracking: _setBlockTracking, createElementVNode: _createElementVNode } = _Vue
|
||||
|
||||
return _cache[0] || (
|
||||
_setBlockTracking(-1),
|
||||
_setBlockTracking(-1, true),
|
||||
(_cache[0] = _createElementVNode("div", { id: foo }, null, 8 /* PROPS */, ["id"])).cacheIndex = 0,
|
||||
_setBlockTracking(1),
|
||||
_cache[0]
|
||||
|
@ -28,7 +28,7 @@ return function render(_ctx, _cache) {
|
|||
|
||||
return (_openBlock(), _createElementBlock("div", null, [
|
||||
_cache[0] || (
|
||||
_setBlockTracking(-1),
|
||||
_setBlockTracking(-1, true),
|
||||
(_cache[0] = _createVNode(_component_Comp, { id: foo }, null, 8 /* PROPS */, ["id"])).cacheIndex = 0,
|
||||
_setBlockTracking(1),
|
||||
_cache[0]
|
||||
|
@ -47,7 +47,7 @@ return function render(_ctx, _cache) {
|
|||
|
||||
return (_openBlock(), _createElementBlock("div", null, [
|
||||
_cache[0] || (
|
||||
_setBlockTracking(-1),
|
||||
_setBlockTracking(-1, true),
|
||||
(_cache[0] = _createElementVNode("div", { id: foo }, null, 8 /* PROPS */, ["id"])).cacheIndex = 0,
|
||||
_setBlockTracking(1),
|
||||
_cache[0]
|
||||
|
@ -66,7 +66,7 @@ return function render(_ctx, _cache) {
|
|||
|
||||
return (_openBlock(), _createElementBlock("div", null, [
|
||||
_cache[0] || (
|
||||
_setBlockTracking(-1),
|
||||
_setBlockTracking(-1, true),
|
||||
(_cache[0] = _renderSlot($slots, "default")).cacheIndex = 0,
|
||||
_setBlockTracking(1),
|
||||
_cache[0]
|
||||
|
@ -85,7 +85,7 @@ return function render(_ctx, _cache) {
|
|||
|
||||
return (_openBlock(), _createElementBlock("div", null, [
|
||||
_cache[0] || (
|
||||
_setBlockTracking(-1),
|
||||
_setBlockTracking(-1, true),
|
||||
(_cache[0] = _createElementVNode("div")).cacheIndex = 0,
|
||||
_setBlockTracking(1),
|
||||
_cache[0]
|
||||
|
|
|
@ -13,6 +13,7 @@ import {
|
|||
type ForNode,
|
||||
type InterpolationNode,
|
||||
NodeTypes,
|
||||
type RootNode,
|
||||
type SimpleExpressionNode,
|
||||
} from '../../src/ast'
|
||||
import { ErrorCodes } from '../../src/errors'
|
||||
|
@ -24,7 +25,10 @@ import { createObjectMatcher } from '../testUtils'
|
|||
export function parseWithForTransform(
|
||||
template: string,
|
||||
options: CompilerOptions = {},
|
||||
) {
|
||||
): {
|
||||
root: RootNode
|
||||
node: ForNode & { codegenNode: ForCodegenNode }
|
||||
} {
|
||||
const ast = parse(template, options)
|
||||
transform(ast, {
|
||||
nodeTransforms: [
|
||||
|
|
|
@ -53,4 +53,12 @@ describe('compiler: v-memo transform', () => {
|
|||
),
|
||||
).toMatchSnapshot()
|
||||
})
|
||||
|
||||
test('element v-for key expression prefixing + v-memo', () => {
|
||||
expect(
|
||||
compile(
|
||||
`<span v-for="data of tableData" :key="getId(data)" v-memo="getLetter(data)"></span>`,
|
||||
),
|
||||
).toMatchSnapshot()
|
||||
})
|
||||
})
|
||||
|
|
|
@ -285,6 +285,21 @@ describe('compiler: transform v-on', () => {
|
|||
},
|
||||
],
|
||||
})
|
||||
|
||||
const { node: node2 } = parseWithVOn(
|
||||
`<div @click="(e: (number | string)[]) => foo(e)"/>`,
|
||||
)
|
||||
expect((node2.codegenNode as VNodeCall).props).toMatchObject({
|
||||
properties: [
|
||||
{
|
||||
key: { content: `onClick` },
|
||||
value: {
|
||||
type: NodeTypes.SIMPLE_EXPRESSION,
|
||||
content: `(e: (number | string)[]) => foo(e)`,
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
})
|
||||
|
||||
test('should NOT wrap as function if expression is already function expression (async)', () => {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import type { TransformContext } from '../src'
|
||||
import type { Position } from '../src/ast'
|
||||
import type { ExpressionNode, TransformContext } from '../src'
|
||||
import { type Position, createSimpleExpression } from '../src/ast'
|
||||
import {
|
||||
advancePositionWithClone,
|
||||
isMemberExpressionBrowser,
|
||||
|
@ -41,7 +41,8 @@ describe('advancePositionWithClone', () => {
|
|||
})
|
||||
|
||||
describe('isMemberExpression', () => {
|
||||
function commonAssertions(fn: (str: string) => boolean) {
|
||||
function commonAssertions(raw: (exp: ExpressionNode) => boolean) {
|
||||
const fn = (str: string) => raw(createSimpleExpression(str))
|
||||
// should work
|
||||
expect(fn('obj.foo')).toBe(true)
|
||||
expect(fn('obj[foo]')).toBe(true)
|
||||
|
@ -78,13 +79,16 @@ describe('isMemberExpression', () => {
|
|||
|
||||
test('browser', () => {
|
||||
commonAssertions(isMemberExpressionBrowser)
|
||||
expect(isMemberExpressionBrowser('123[a]')).toBe(false)
|
||||
expect(isMemberExpressionBrowser(createSimpleExpression('123[a]'))).toBe(
|
||||
false,
|
||||
)
|
||||
})
|
||||
|
||||
test('node', () => {
|
||||
const ctx = { expressionPlugins: ['typescript'] } as any as TransformContext
|
||||
const fn = (str: string) => isMemberExpressionNode(str, ctx)
|
||||
commonAssertions(fn)
|
||||
const fn = (str: string) =>
|
||||
isMemberExpressionNode(createSimpleExpression(str), ctx)
|
||||
commonAssertions(exp => isMemberExpressionNode(exp, ctx))
|
||||
|
||||
// TS-specific checks
|
||||
expect(fn('foo as string')).toBe(true)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@vue/compiler-core",
|
||||
"version": "3.5.0-beta.1",
|
||||
"version": "3.5.13",
|
||||
"description": "@vue/compiler-core",
|
||||
"main": "index.js",
|
||||
"module": "dist/compiler-core.esm-bundler.js",
|
||||
|
@ -48,7 +48,7 @@
|
|||
"dependencies": {
|
||||
"@babel/parser": "catalog:",
|
||||
"@vue/shared": "workspace:*",
|
||||
"entities": "^5.0.0",
|
||||
"entities": "^4.5.0",
|
||||
"estree-walker": "catalog:",
|
||||
"source-map-js": "catalog:"
|
||||
},
|
||||
|
|
|
@ -203,7 +203,7 @@ export interface DirectiveNode extends Node {
|
|||
rawName?: string
|
||||
exp: ExpressionNode | undefined
|
||||
arg: ExpressionNode | undefined
|
||||
modifiers: string[]
|
||||
modifiers: SimpleExpressionNode[]
|
||||
/**
|
||||
* optional property to cache the expression parse result for v-for
|
||||
*/
|
||||
|
@ -418,6 +418,7 @@ export interface CacheExpression extends Node {
|
|||
index: number
|
||||
value: JSChildNode
|
||||
needPauseTracking: boolean
|
||||
inVOnce: boolean
|
||||
needArraySpread: boolean
|
||||
}
|
||||
|
||||
|
@ -774,12 +775,14 @@ export function createCacheExpression(
|
|||
index: number,
|
||||
value: JSChildNode,
|
||||
needPauseTracking: boolean = false,
|
||||
inVOnce: boolean = false,
|
||||
): CacheExpression {
|
||||
return {
|
||||
type: NodeTypes.JS_CACHE_EXPRESSION,
|
||||
index,
|
||||
value,
|
||||
needPauseTracking: needPauseTracking,
|
||||
inVOnce,
|
||||
needArraySpread: false,
|
||||
loc: locStub,
|
||||
}
|
||||
|
|
|
@ -99,7 +99,7 @@ interface MappingItem {
|
|||
name: string | null
|
||||
}
|
||||
|
||||
const PURE_ANNOTATION = `/*#__PURE__*/`
|
||||
const PURE_ANNOTATION = `/*@__PURE__*/`
|
||||
|
||||
const aliasHelper = (s: symbol) => `${helperNameMap[s]}: _${helperNameMap[s]}`
|
||||
|
||||
|
@ -725,7 +725,7 @@ function genNode(node: CodegenNode | symbol | string, context: CodegenContext) {
|
|||
!__BROWSER__ && genReturnStatement(node, context)
|
||||
break
|
||||
|
||||
/* istanbul ignore next */
|
||||
/* v8 ignore start */
|
||||
case NodeTypes.IF_BRANCH:
|
||||
// noop
|
||||
break
|
||||
|
@ -736,6 +736,7 @@ function genNode(node: CodegenNode | symbol | string, context: CodegenContext) {
|
|||
const exhaustiveCheck: never = node
|
||||
return exhaustiveCheck
|
||||
}
|
||||
/* v8 ignore stop */
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1016,7 +1017,9 @@ function genCacheExpression(node: CacheExpression, context: CodegenContext) {
|
|||
push(`_cache[${node.index}] || (`)
|
||||
if (needPauseTracking) {
|
||||
indent()
|
||||
push(`${helper(SET_BLOCK_TRACKING)}(-1),`)
|
||||
push(`${helper(SET_BLOCK_TRACKING)}(-1`)
|
||||
if (node.inVOnce) push(`, true`)
|
||||
push(`),`)
|
||||
newline()
|
||||
push(`(`)
|
||||
}
|
||||
|
|
|
@ -68,7 +68,7 @@ export function baseCompile(
|
|||
): CodegenResult {
|
||||
const onError = options.onError || defaultOnError
|
||||
const isModuleMode = options.mode === 'module'
|
||||
/* istanbul ignore if */
|
||||
/* v8 ignore start */
|
||||
if (__BROWSER__) {
|
||||
if (options.prefixIdentifiers === true) {
|
||||
onError(createCompilerError(ErrorCodes.X_PREFIX_ID_NOT_SUPPORTED))
|
||||
|
@ -76,6 +76,7 @@ export function baseCompile(
|
|||
onError(createCompilerError(ErrorCodes.X_MODULE_MODE_NOT_SUPPORTED))
|
||||
}
|
||||
}
|
||||
/* v8 ignore stop */
|
||||
|
||||
const prefixIdentifiers =
|
||||
!__BROWSER__ && (options.prefixIdentifiers === true || isModuleMode)
|
||||
|
|
|
@ -52,6 +52,11 @@ export interface ParserOptions
|
|||
* e.g. elements that should preserve whitespace inside, e.g. `<pre>`
|
||||
*/
|
||||
isPreTag?: (tag: string) => boolean
|
||||
/**
|
||||
* Elements that should ignore the first newline token per parinsg spec
|
||||
* e.g. `<textarea>` and `<pre>`
|
||||
*/
|
||||
isIgnoreNewlineTag?: (tag: string) => boolean
|
||||
/**
|
||||
* Platform-specific built-in components e.g. `<Transition>`
|
||||
*/
|
||||
|
|
|
@ -44,7 +44,7 @@ import {
|
|||
isSimpleIdentifier,
|
||||
isStaticArgOf,
|
||||
} from './utils'
|
||||
import { decodeHTML } from 'entities/dist/decode.js'
|
||||
import { decodeHTML } from 'entities/lib/decode.js'
|
||||
import {
|
||||
type ParserOptions as BabelOptions,
|
||||
parse,
|
||||
|
@ -72,6 +72,7 @@ export const defaultParserOptions: MergedParserOptions = {
|
|||
getNamespace: () => Namespaces.HTML,
|
||||
isVoidTag: NO,
|
||||
isPreTag: NO,
|
||||
isIgnoreNewlineTag: NO,
|
||||
isCustomElement: NO,
|
||||
onError: defaultOnError,
|
||||
onWarn: defaultOnWarn,
|
||||
|
@ -225,7 +226,7 @@ const tokenizer = new Tokenizer(stack, {
|
|||
rawName: raw,
|
||||
exp: undefined,
|
||||
arg: undefined,
|
||||
modifiers: raw === '.' ? ['prop'] : [],
|
||||
modifiers: raw === '.' ? [createSimpleExpression('prop')] : [],
|
||||
loc: getLoc(start),
|
||||
}
|
||||
if (name === 'pre') {
|
||||
|
@ -273,7 +274,8 @@ const tokenizer = new Tokenizer(stack, {
|
|||
setLocEnd(arg.loc, end)
|
||||
}
|
||||
} else {
|
||||
;(currentProp as DirectiveNode).modifiers.push(mod)
|
||||
const exp = createSimpleExpression(mod, true, getLoc(start, end))
|
||||
;(currentProp as DirectiveNode).modifiers.push(exp)
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -379,7 +381,9 @@ const tokenizer = new Tokenizer(stack, {
|
|||
if (
|
||||
__COMPAT__ &&
|
||||
currentProp.name === 'bind' &&
|
||||
(syncIndex = currentProp.modifiers.indexOf('sync')) > -1 &&
|
||||
(syncIndex = currentProp.modifiers.findIndex(
|
||||
mod => mod.content === 'sync',
|
||||
)) > -1 &&
|
||||
checkCompatEnabled(
|
||||
CompilerDeprecationTypes.COMPILER_V_BIND_SYNC,
|
||||
currentOptions,
|
||||
|
@ -630,7 +634,7 @@ function onCloseTag(el: ElementNode, end: number, isImplied = false) {
|
|||
}
|
||||
|
||||
// refine element type
|
||||
const { tag, ns } = el
|
||||
const { tag, ns, children } = el
|
||||
if (!inVPre) {
|
||||
if (tag === 'slot') {
|
||||
el.tagType = ElementTypes.SLOT
|
||||
|
@ -643,8 +647,18 @@ function onCloseTag(el: ElementNode, end: number, isImplied = false) {
|
|||
|
||||
// whitespace management
|
||||
if (!tokenizer.inRCDATA) {
|
||||
el.children = condenseWhitespace(el.children, el.tag)
|
||||
el.children = condenseWhitespace(children, tag)
|
||||
}
|
||||
|
||||
if (ns === Namespaces.HTML && currentOptions.isIgnoreNewlineTag(tag)) {
|
||||
// remove leading newline for <textarea> and <pre> per html spec
|
||||
// https://html.spec.whatwg.org/multipage/parsing.html#parsing-main-inbody
|
||||
const first = children[0]
|
||||
if (first && first.type === NodeTypes.TEXT) {
|
||||
first.content = first.content.replace(/^\r?\n/, '')
|
||||
}
|
||||
}
|
||||
|
||||
if (ns === Namespaces.HTML && currentOptions.isPreTag(tag)) {
|
||||
inPre--
|
||||
}
|
||||
|
@ -866,14 +880,6 @@ function condenseWhitespace(
|
|||
}
|
||||
}
|
||||
}
|
||||
if (inPre && tag && currentOptions.isPreTag(tag)) {
|
||||
// remove leading newline per html spec
|
||||
// https://html.spec.whatwg.org/multipage/grouping-content.html#the-pre-element
|
||||
const first = nodes[0]
|
||||
if (first && first.type === NodeTypes.TEXT) {
|
||||
first.content = first.content.replace(/^\r?\n/, '')
|
||||
}
|
||||
}
|
||||
return removedWhitespace ? nodes.filter(Boolean) : nodes
|
||||
}
|
||||
|
||||
|
@ -927,6 +933,10 @@ function getLoc(start: number, end?: number): SourceLocation {
|
|||
}
|
||||
}
|
||||
|
||||
export function cloneLoc(loc: SourceLocation): SourceLocation {
|
||||
return getLoc(loc.start.offset, loc.end.offset)
|
||||
}
|
||||
|
||||
function setLocEnd(loc: SourceLocation, end: number) {
|
||||
loc.end = tokenizer.getPos(end)
|
||||
loc.source = getSlice(loc.start.offset, end)
|
||||
|
|
|
@ -36,7 +36,7 @@ import {
|
|||
EntityDecoder,
|
||||
fromCodePoint,
|
||||
htmlDecodeTree,
|
||||
} from 'entities/dist/decode.js'
|
||||
} from 'entities/lib/decode.js'
|
||||
|
||||
export enum ParseMode {
|
||||
BASE,
|
||||
|
@ -438,7 +438,7 @@ export default class Tokenizer {
|
|||
// We have to parse entities in <title> and <textarea> tags.
|
||||
if (!__BROWSER__ && c === CharCodes.Amp) {
|
||||
this.startEntity()
|
||||
} else if (c === this.delimiterOpen[0]) {
|
||||
} else if (!this.inVPre && c === this.delimiterOpen[0]) {
|
||||
// We also need to handle interpolation
|
||||
this.state = State.InterpolationOpen
|
||||
this.delimiterIndex = 0
|
||||
|
|
|
@ -23,7 +23,6 @@ import {
|
|||
import {
|
||||
EMPTY_OBJ,
|
||||
NOOP,
|
||||
PatchFlagNames,
|
||||
PatchFlags,
|
||||
camelize,
|
||||
capitalize,
|
||||
|
@ -117,7 +116,7 @@ export interface TransformContext
|
|||
addIdentifiers(exp: ExpressionNode | string): void
|
||||
removeIdentifiers(exp: ExpressionNode | string): void
|
||||
hoist(exp: string | JSChildNode | ArrayExpression): SimpleExpressionNode
|
||||
cache(exp: JSChildNode, isVNode?: boolean): CacheExpression
|
||||
cache(exp: JSChildNode, isVNode?: boolean, inVOnce?: boolean): CacheExpression
|
||||
constantCache: WeakMap<TemplateChildNode, ConstantTypes>
|
||||
|
||||
// 2.x Compat only
|
||||
|
@ -222,7 +221,7 @@ export function createTransformContext(
|
|||
return `_${helperNameMap[context.helper(name)]}`
|
||||
},
|
||||
replaceNode(node) {
|
||||
/* istanbul ignore if */
|
||||
/* v8 ignore start */
|
||||
if (__DEV__) {
|
||||
if (!context.currentNode) {
|
||||
throw new Error(`Node being replaced is already removed.`)
|
||||
|
@ -231,9 +230,11 @@ export function createTransformContext(
|
|||
throw new Error(`Cannot replace root node.`)
|
||||
}
|
||||
}
|
||||
/* v8 ignore stop */
|
||||
context.parent!.children[context.childIndex] = context.currentNode = node
|
||||
},
|
||||
removeNode(node) {
|
||||
/* v8 ignore next 3 */
|
||||
if (__DEV__ && !context.parent) {
|
||||
throw new Error(`Cannot remove root node.`)
|
||||
}
|
||||
|
@ -243,7 +244,7 @@ export function createTransformContext(
|
|||
: context.currentNode
|
||||
? context.childIndex
|
||||
: -1
|
||||
/* istanbul ignore if */
|
||||
/* v8 ignore next 3 */
|
||||
if (__DEV__ && removalIndex < 0) {
|
||||
throw new Error(`node being removed is not a child of current parent`)
|
||||
}
|
||||
|
@ -296,11 +297,12 @@ export function createTransformContext(
|
|||
identifier.hoisted = exp
|
||||
return identifier
|
||||
},
|
||||
cache(exp, isVNode = false) {
|
||||
cache(exp, isVNode = false, inVOnce = false) {
|
||||
const cacheExp = createCacheExpression(
|
||||
context.cached.length,
|
||||
exp,
|
||||
isVNode,
|
||||
inVOnce,
|
||||
)
|
||||
context.cached.push(cacheExp)
|
||||
return cacheExp
|
||||
|
@ -373,7 +375,6 @@ function createRootCodegen(root: RootNode, context: TransformContext) {
|
|||
} else if (children.length > 1) {
|
||||
// root has multiple nodes - return a fragment block.
|
||||
let patchFlag = PatchFlags.STABLE_FRAGMENT
|
||||
let patchFlagText = PatchFlagNames[PatchFlags.STABLE_FRAGMENT]
|
||||
// check if the fragment actually contains a single valid child with
|
||||
// the rest being comments
|
||||
if (
|
||||
|
@ -381,7 +382,6 @@ function createRootCodegen(root: RootNode, context: TransformContext) {
|
|||
children.filter(c => c.type !== NodeTypes.COMMENT).length === 1
|
||||
) {
|
||||
patchFlag |= PatchFlags.DEV_ROOT_FRAGMENT
|
||||
patchFlagText += `, ${PatchFlagNames[PatchFlags.DEV_ROOT_FRAGMENT]}`
|
||||
}
|
||||
root.codegenNode = createVNodeCall(
|
||||
context,
|
||||
|
|
|
@ -665,7 +665,7 @@ export function buildProps(
|
|||
}
|
||||
|
||||
// force hydration for v-bind with .prop modifier
|
||||
if (isVBind && modifiers.includes('prop')) {
|
||||
if (isVBind && modifiers.some(mod => mod.content === 'prop')) {
|
||||
patchFlag |= PatchFlags.NEED_HYDRATION
|
||||
}
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@ import {
|
|||
isStaticPropertyKey,
|
||||
walkIdentifiers,
|
||||
} from '../babelUtils'
|
||||
import { advancePositionWithClone, isSimpleIdentifier } from '../utils'
|
||||
import { advancePositionWithClone, findDir, isSimpleIdentifier } from '../utils'
|
||||
import {
|
||||
genPropsAccessExp,
|
||||
hasOwn,
|
||||
|
@ -44,7 +44,7 @@ import { parseExpression } from '@babel/parser'
|
|||
import { IS_REF, UNREF } from '../runtimeHelpers'
|
||||
import { BindingTypes } from '../options'
|
||||
|
||||
const isLiteralWhitelisted = /*#__PURE__*/ makeMap('true,false,null,this')
|
||||
const isLiteralWhitelisted = /*@__PURE__*/ makeMap('true,false,null,this')
|
||||
|
||||
export const transformExpression: NodeTransform = (node, context) => {
|
||||
if (node.type === NodeTypes.INTERPOLATION) {
|
||||
|
@ -54,6 +54,7 @@ export const transformExpression: NodeTransform = (node, context) => {
|
|||
)
|
||||
} else if (node.type === NodeTypes.ELEMENT) {
|
||||
// handle directives on element
|
||||
const memo = findDir(node, 'memo')
|
||||
for (let i = 0; i < node.props.length; i++) {
|
||||
const dir = node.props[i]
|
||||
// do not process for v-on & v-for since they are special handled
|
||||
|
@ -65,7 +66,14 @@ export const transformExpression: NodeTransform = (node, context) => {
|
|||
if (
|
||||
exp &&
|
||||
exp.type === NodeTypes.SIMPLE_EXPRESSION &&
|
||||
!(dir.name === 'on' && arg)
|
||||
!(dir.name === 'on' && arg) &&
|
||||
// key has been processed in transformFor(vMemo + vFor)
|
||||
!(
|
||||
memo &&
|
||||
arg &&
|
||||
arg.type === NodeTypes.SIMPLE_EXPRESSION &&
|
||||
arg.content === 'key'
|
||||
)
|
||||
) {
|
||||
dir.exp = processExpression(
|
||||
exp,
|
||||
|
|
|
@ -12,7 +12,7 @@ import { camelize } from '@vue/shared'
|
|||
import { CAMELIZE } from '../runtimeHelpers'
|
||||
import { processExpression } from './transformExpression'
|
||||
|
||||
// v-bind without arg is handled directly in ./transformElements.ts due to it affecting
|
||||
// v-bind without arg is handled directly in ./transformElement.ts due to its affecting
|
||||
// codegen for the entire props object. This transform here is only for v-bind
|
||||
// *with* args.
|
||||
export const transformBind: DirectiveTransform = (dir, _node, context) => {
|
||||
|
@ -69,7 +69,7 @@ export const transformBind: DirectiveTransform = (dir, _node, context) => {
|
|||
}
|
||||
|
||||
// .sync is replaced by v-model:arg
|
||||
if (modifiers.includes('camel')) {
|
||||
if (modifiers.some(mod => mod.content === 'camel')) {
|
||||
if (arg.type === NodeTypes.SIMPLE_EXPRESSION) {
|
||||
if (arg.isStatic) {
|
||||
arg.content = camelize(arg.content)
|
||||
|
@ -83,10 +83,10 @@ export const transformBind: DirectiveTransform = (dir, _node, context) => {
|
|||
}
|
||||
|
||||
if (!context.inSSR) {
|
||||
if (modifiers.includes('prop')) {
|
||||
if (modifiers.some(mod => mod.content === 'prop')) {
|
||||
injectPrefix(arg, '.')
|
||||
}
|
||||
if (modifiers.includes('attr')) {
|
||||
if (modifiers.some(mod => mod.content === 'attr')) {
|
||||
injectPrefix(arg, '^')
|
||||
}
|
||||
}
|
||||
|
|
|
@ -63,17 +63,27 @@ export const transformFor: NodeTransform = createStructuralDirectiveTransform(
|
|||
const isTemplate = isTemplateNode(node)
|
||||
const memo = findDir(node, 'memo')
|
||||
const keyProp = findProp(node, `key`, false, true)
|
||||
if (keyProp && keyProp.type === NodeTypes.DIRECTIVE && !keyProp.exp) {
|
||||
const isDirKey = keyProp && keyProp.type === NodeTypes.DIRECTIVE
|
||||
if (isDirKey && !keyProp.exp) {
|
||||
// resolve :key shorthand #10882
|
||||
transformBindShorthand(keyProp, context)
|
||||
}
|
||||
const keyExp =
|
||||
let keyExp =
|
||||
keyProp &&
|
||||
(keyProp.type === NodeTypes.ATTRIBUTE
|
||||
? keyProp.value
|
||||
? createSimpleExpression(keyProp.value.content, true)
|
||||
: undefined
|
||||
: keyProp.exp)
|
||||
|
||||
if (memo && keyExp && isDirKey) {
|
||||
if (!__BROWSER__) {
|
||||
keyProp.exp = keyExp = processExpression(
|
||||
keyExp as SimpleExpressionNode,
|
||||
context,
|
||||
)
|
||||
}
|
||||
}
|
||||
const keyProperty =
|
||||
keyProp && keyExp ? createObjectProperty(`key`, keyExp) : null
|
||||
|
||||
|
|
|
@ -30,9 +30,10 @@ import {
|
|||
import { ErrorCodes, createCompilerError } from '../errors'
|
||||
import { processExpression } from './transformExpression'
|
||||
import { validateBrowserExpression } from '../validateExpression'
|
||||
import { cloneLoc } from '../parser'
|
||||
import { CREATE_COMMENT, FRAGMENT } from '../runtimeHelpers'
|
||||
import { findDir, findProp, getMemoedVNodeCall, injectProp } from '../utils'
|
||||
import { PatchFlagNames, PatchFlags } from '@vue/shared'
|
||||
import { PatchFlags } from '@vue/shared'
|
||||
|
||||
export const transformIf: NodeTransform = createStructuralDirectiveTransform(
|
||||
/^(if|else|else-if)$/,
|
||||
|
@ -110,7 +111,7 @@ export function processIf(
|
|||
const branch = createIfBranch(node, dir)
|
||||
const ifNode: IfNode = {
|
||||
type: NodeTypes.IF,
|
||||
loc: node.loc,
|
||||
loc: cloneLoc(node.loc),
|
||||
branches: [branch],
|
||||
}
|
||||
context.replaceNode(ifNode)
|
||||
|
@ -264,7 +265,6 @@ function createChildrenCodegenNode(
|
|||
return vnodeCall
|
||||
} else {
|
||||
let patchFlag = PatchFlags.STABLE_FRAGMENT
|
||||
let patchFlagText = PatchFlagNames[PatchFlags.STABLE_FRAGMENT]
|
||||
// check if the fragment actually contains a single valid child with
|
||||
// the rest being comments
|
||||
if (
|
||||
|
@ -273,7 +273,6 @@ function createChildrenCodegenNode(
|
|||
children.filter(c => c.type !== NodeTypes.COMMENT).length === 1
|
||||
) {
|
||||
patchFlag |= PatchFlags.DEV_ROOT_FRAGMENT
|
||||
patchFlagText += `, ${PatchFlagNames[PatchFlags.DEV_ROOT_FRAGMENT]}`
|
||||
}
|
||||
|
||||
return createVNodeCall(
|
||||
|
|
|
@ -31,7 +31,7 @@ export const transformModel: DirectiveTransform = (dir, node, context) => {
|
|||
|
||||
// we assume v-model directives are always parsed
|
||||
// (not artificially created by a transform)
|
||||
const rawExp = exp.loc.source
|
||||
const rawExp = exp.loc.source.trim()
|
||||
const expString =
|
||||
exp.type === NodeTypes.SIMPLE_EXPRESSION ? exp.content : rawExp
|
||||
|
||||
|
@ -55,10 +55,7 @@ export const transformModel: DirectiveTransform = (dir, node, context) => {
|
|||
bindingType === BindingTypes.SETUP_REF ||
|
||||
bindingType === BindingTypes.SETUP_MAYBE_REF)
|
||||
|
||||
if (
|
||||
!expString.trim() ||
|
||||
(!isMemberExpression(expString, context) && !maybeRef)
|
||||
) {
|
||||
if (!expString.trim() || (!isMemberExpression(exp, context) && !maybeRef)) {
|
||||
context.onError(
|
||||
createCompilerError(ErrorCodes.X_V_MODEL_MALFORMED_EXPRESSION, exp.loc),
|
||||
)
|
||||
|
@ -134,6 +131,7 @@ export const transformModel: DirectiveTransform = (dir, node, context) => {
|
|||
// modelModifiers: { foo: true, "bar-baz": true }
|
||||
if (dir.modifiers.length && node.tagType === ElementTypes.COMPONENT) {
|
||||
const modifiers = dir.modifiers
|
||||
.map(m => m.content)
|
||||
.map(m => (isSimpleIdentifier(m) ? m : JSON.stringify(m)) + `: true`)
|
||||
.join(`, `)
|
||||
const modifiersKey = arg
|
||||
|
|
|
@ -13,14 +13,11 @@ import { camelize, toHandlerKey } from '@vue/shared'
|
|||
import { ErrorCodes, createCompilerError } from '../errors'
|
||||
import { processExpression } from './transformExpression'
|
||||
import { validateBrowserExpression } from '../validateExpression'
|
||||
import { hasScopeRef, isMemberExpression } from '../utils'
|
||||
import { hasScopeRef, isFnExpression, isMemberExpression } from '../utils'
|
||||
import { TO_HANDLER_KEY } from '../runtimeHelpers'
|
||||
|
||||
const fnExpRE =
|
||||
/^\s*(async\s*)?(\([^)]*?\)|[\w$_]+)\s*(:[^=]+)?=>|^\s*(async\s+)?function(?:\s+[\w$]+)?\s*\(/
|
||||
|
||||
export interface VOnDirectiveNode extends DirectiveNode {
|
||||
// v-on without arg is handled directly in ./transformElements.ts due to it affecting
|
||||
// v-on without arg is handled directly in ./transformElement.ts due to its affecting
|
||||
// codegen for the entire props object. This transform here is only for v-on
|
||||
// *with* args.
|
||||
arg: ExpressionNode
|
||||
|
@ -84,8 +81,8 @@ export const transformOn: DirectiveTransform = (
|
|||
}
|
||||
let shouldCache: boolean = context.cacheHandlers && !exp && !context.inVOnce
|
||||
if (exp) {
|
||||
const isMemberExp = isMemberExpression(exp.content, context)
|
||||
const isInlineStatement = !(isMemberExp || fnExpRE.test(exp.content))
|
||||
const isMemberExp = isMemberExpression(exp, context)
|
||||
const isInlineStatement = !(isMemberExp || isFnExpression(exp, context))
|
||||
const hasMultipleStatements = exp.content.includes(`;`)
|
||||
|
||||
// process the expression since it's been skipped
|
||||
|
|
|
@ -17,7 +17,11 @@ export const transformOnce: NodeTransform = (node, context) => {
|
|||
context.inVOnce = false
|
||||
const cur = context.currentNode as ElementNode | IfNode | ForNode
|
||||
if (cur.codegenNode) {
|
||||
cur.codegenNode = context.cache(cur.codegenNode, true /* isVNode */)
|
||||
cur.codegenNode = context.cache(
|
||||
cur.codegenNode,
|
||||
true /* isVNode */,
|
||||
true /* inVOnce */,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -40,7 +40,7 @@ import {
|
|||
import { NOOP, isObject, isString } from '@vue/shared'
|
||||
import type { PropsExpression } from './transforms/transformElement'
|
||||
import { parseExpression } from '@babel/parser'
|
||||
import type { Expression } from '@babel/types'
|
||||
import type { Expression, Node } from '@babel/types'
|
||||
import { unwrapTSNode } from './babelUtils'
|
||||
|
||||
export const isStaticExp = (p: JSChildNode): p is SimpleExpressionNode =>
|
||||
|
@ -78,15 +78,20 @@ const validFirstIdentCharRE = /[A-Za-z_$\xA0-\uFFFF]/
|
|||
const validIdentCharRE = /[\.\?\w$\xA0-\uFFFF]/
|
||||
const whitespaceRE = /\s+[.[]\s*|\s*[.[]\s+/g
|
||||
|
||||
const getExpSource = (exp: ExpressionNode): string =>
|
||||
exp.type === NodeTypes.SIMPLE_EXPRESSION ? exp.content : exp.loc.source
|
||||
|
||||
/**
|
||||
* Simple lexer to check if an expression is a member expression. This is
|
||||
* lax and only checks validity at the root level (i.e. does not validate exps
|
||||
* inside square brackets), but it's ok since these are only used on template
|
||||
* expressions and false positives are invalid expressions in the first place.
|
||||
*/
|
||||
export const isMemberExpressionBrowser = (path: string): boolean => {
|
||||
export const isMemberExpressionBrowser = (exp: ExpressionNode): boolean => {
|
||||
// remove whitespaces around . or [ first
|
||||
path = path.trim().replace(whitespaceRE, s => s.trim())
|
||||
const path = getExpSource(exp)
|
||||
.trim()
|
||||
.replace(whitespaceRE, s => s.trim())
|
||||
|
||||
let state = MemberExpLexState.inMemberExp
|
||||
let stateStack: MemberExpLexState[] = []
|
||||
|
@ -154,15 +159,19 @@ export const isMemberExpressionBrowser = (path: string): boolean => {
|
|||
}
|
||||
|
||||
export const isMemberExpressionNode: (
|
||||
path: string,
|
||||
exp: ExpressionNode,
|
||||
context: TransformContext,
|
||||
) => boolean = __BROWSER__
|
||||
? (NOOP as any)
|
||||
: (path: string, context: TransformContext): boolean => {
|
||||
: (exp, context) => {
|
||||
try {
|
||||
let ret: Expression = parseExpression(path, {
|
||||
plugins: context.expressionPlugins,
|
||||
})
|
||||
let ret: Node =
|
||||
exp.ast ||
|
||||
parseExpression(getExpSource(exp), {
|
||||
plugins: context.expressionPlugins
|
||||
? [...context.expressionPlugins, 'typescript']
|
||||
: ['typescript'],
|
||||
})
|
||||
ret = unwrapTSNode(ret) as Expression
|
||||
return (
|
||||
ret.type === 'MemberExpression' ||
|
||||
|
@ -175,10 +184,52 @@ export const isMemberExpressionNode: (
|
|||
}
|
||||
|
||||
export const isMemberExpression: (
|
||||
path: string,
|
||||
exp: ExpressionNode,
|
||||
context: TransformContext,
|
||||
) => boolean = __BROWSER__ ? isMemberExpressionBrowser : isMemberExpressionNode
|
||||
|
||||
const fnExpRE =
|
||||
/^\s*(async\s*)?(\([^)]*?\)|[\w$_]+)\s*(:[^=]+)?=>|^\s*(async\s+)?function(?:\s+[\w$]+)?\s*\(/
|
||||
|
||||
export const isFnExpressionBrowser: (exp: ExpressionNode) => boolean = exp =>
|
||||
fnExpRE.test(getExpSource(exp))
|
||||
|
||||
export const isFnExpressionNode: (
|
||||
exp: ExpressionNode,
|
||||
context: TransformContext,
|
||||
) => boolean = __BROWSER__
|
||||
? (NOOP as any)
|
||||
: (exp, context) => {
|
||||
try {
|
||||
let ret: Node =
|
||||
exp.ast ||
|
||||
parseExpression(getExpSource(exp), {
|
||||
plugins: context.expressionPlugins
|
||||
? [...context.expressionPlugins, 'typescript']
|
||||
: ['typescript'],
|
||||
})
|
||||
// parser may parse the exp as statements when it contains semicolons
|
||||
if (ret.type === 'Program') {
|
||||
ret = ret.body[0]
|
||||
if (ret.type === 'ExpressionStatement') {
|
||||
ret = ret.expression
|
||||
}
|
||||
}
|
||||
ret = unwrapTSNode(ret) as Expression
|
||||
return (
|
||||
ret.type === 'FunctionExpression' ||
|
||||
ret.type === 'ArrowFunctionExpression'
|
||||
)
|
||||
} catch (e) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
export const isFnExpression: (
|
||||
exp: ExpressionNode,
|
||||
context: TransformContext,
|
||||
) => boolean = __BROWSER__ ? isFnExpressionBrowser : isFnExpressionNode
|
||||
|
||||
export function advancePositionWithClone(
|
||||
pos: Position,
|
||||
source: string,
|
||||
|
@ -222,7 +273,7 @@ export function advancePositionWithMutation(
|
|||
}
|
||||
|
||||
export function assert(condition: boolean, msg?: string): void {
|
||||
/* istanbul ignore if */
|
||||
/* v8 ignore next 3 */
|
||||
if (!condition) {
|
||||
throw new Error(msg || `unexpected compiler condition`)
|
||||
}
|
||||
|
|
|
@ -32,6 +32,22 @@ describe('DOM parser', () => {
|
|||
})
|
||||
})
|
||||
|
||||
test('<textarea> should remove leading newline', () => {
|
||||
const ast = parse('<textarea>\nhello</textarea>', parserOptions)
|
||||
const element = ast.children[0] as ElementNode
|
||||
const text = element.children[0] as TextNode
|
||||
expect(element.children.length).toBe(1)
|
||||
expect(text).toStrictEqual({
|
||||
type: NodeTypes.TEXT,
|
||||
content: 'hello',
|
||||
loc: {
|
||||
start: { offset: 10, line: 1, column: 11 },
|
||||
end: { offset: 16, line: 2, column: 6 },
|
||||
source: '\nhello',
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
test('should not treat Uppercase component as special tag', () => {
|
||||
const ast = parse(
|
||||
'<TextArea>some<div>text</div>and<!--comment--></TextArea>',
|
||||
|
|
|
@ -1,5 +1,17 @@
|
|||
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||
|
||||
exports[`stringify static html > eligible content (elements > 20) + non-eligible content 1`] = `
|
||||
"const { createElementVNode: _createElementVNode, createStaticVNode: _createStaticVNode, openBlock: _openBlock, createElementBlock: _createElementBlock } = Vue
|
||||
|
||||
return function render(_ctx, _cache) {
|
||||
return (_openBlock(), _createElementBlock("div", null, _cache[0] || (_cache[0] = [
|
||||
_createStaticVNode("<span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span>", 20),
|
||||
_createElementVNode("div", { key: "1" }, "1", -1 /* HOISTED */),
|
||||
_createStaticVNode("<span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span>", 20)
|
||||
])))
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`stringify static html > escape 1`] = `
|
||||
"const { toDisplayString: _toDisplayString, normalizeClass: _normalizeClass, createElementVNode: _createElementVNode, createStaticVNode: _createStaticVNode, openBlock: _openBlock, createElementBlock: _createElementBlock } = Vue
|
||||
|
||||
|
@ -20,6 +32,33 @@ return function render(_ctx, _cache) {
|
|||
}"
|
||||
`;
|
||||
|
||||
exports[`stringify static html > serializing template string style 1`] = `
|
||||
"const { toDisplayString: _toDisplayString, normalizeClass: _normalizeClass, createElementVNode: _createElementVNode, createStaticVNode: _createStaticVNode, openBlock: _openBlock, createElementBlock: _createElementBlock } = Vue
|
||||
|
||||
return function render(_ctx, _cache) {
|
||||
return (_openBlock(), _createElementBlock("div", null, _cache[0] || (_cache[0] = [
|
||||
_createStaticVNode("<div style=\\"color:red;\\"><span class=\\"foo bar\\">1 + false</span><span class=\\"foo bar\\">1 + false</span><span class=\\"foo bar\\">1 + false</span><span class=\\"foo bar\\">1 + false</span><span class=\\"foo bar\\">1 + false</span></div>", 1)
|
||||
])))
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`stringify static html > should bail for <option> elements with null values 1`] = `
|
||||
"const { createElementVNode: _createElementVNode, openBlock: _openBlock, createElementBlock: _createElementBlock } = Vue
|
||||
|
||||
return function render(_ctx, _cache) {
|
||||
return (_openBlock(), _createElementBlock("div", null, _cache[0] || (_cache[0] = [
|
||||
_createElementVNode("select", null, [
|
||||
_createElementVNode("option", { value: null }),
|
||||
_createElementVNode("option", { value: "1" }),
|
||||
_createElementVNode("option", { value: "1" }),
|
||||
_createElementVNode("option", { value: "1" }),
|
||||
_createElementVNode("option", { value: "1" }),
|
||||
_createElementVNode("option", { value: "1" })
|
||||
], -1 /* HOISTED */)
|
||||
])))
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`stringify static html > should bail for <option> elements with number values 1`] = `
|
||||
"const { createElementVNode: _createElementVNode, openBlock: _openBlock, createElementBlock: _createElementBlock } = Vue
|
||||
|
||||
|
|
|
@ -162,6 +162,27 @@ describe('stringify static html', () => {
|
|||
expect(code).toMatchSnapshot()
|
||||
})
|
||||
|
||||
// #12391
|
||||
test('serializing template string style', () => {
|
||||
const { ast, code } = compileWithStringify(
|
||||
`<div><div :style="\`color:red;\`">${repeat(
|
||||
`<span :class="[{ foo: true }, { bar: true }]">{{ 1 }} + {{ false }}</span>`,
|
||||
StringifyThresholds.ELEMENT_WITH_BINDING_COUNT,
|
||||
)}</div></div>`,
|
||||
)
|
||||
// should be optimized now
|
||||
expect(ast.cached).toMatchObject([
|
||||
cachedArrayStaticNodeMatcher(
|
||||
`<div style="color:red;">${repeat(
|
||||
`<span class="foo bar">1 + false</span>`,
|
||||
StringifyThresholds.ELEMENT_WITH_BINDING_COUNT,
|
||||
)}</div>`,
|
||||
1,
|
||||
),
|
||||
])
|
||||
expect(code).toMatchSnapshot()
|
||||
})
|
||||
|
||||
test('escape', () => {
|
||||
const { ast, code } = compileWithStringify(
|
||||
`<div><div>${repeat(
|
||||
|
@ -389,6 +410,24 @@ describe('stringify static html', () => {
|
|||
])
|
||||
})
|
||||
|
||||
test('should stringify mathML', () => {
|
||||
const math = `<math xmlns="http://www.w3.org/1998/Math/MathML">`
|
||||
const repeated = `<ms>1</ms>`
|
||||
const { ast } = compileWithStringify(
|
||||
`<div>${math}${repeat(
|
||||
repeated,
|
||||
StringifyThresholds.NODE_COUNT,
|
||||
)}</math></div>`,
|
||||
)
|
||||
|
||||
expect(ast.cached).toMatchObject([
|
||||
cachedArrayStaticNodeMatcher(
|
||||
`${math}${repeat(repeated, StringifyThresholds.NODE_COUNT)}</math>`,
|
||||
1,
|
||||
),
|
||||
])
|
||||
})
|
||||
|
||||
// #5439
|
||||
test('stringify v-html', () => {
|
||||
const { code } = compileWithStringify(`
|
||||
|
@ -451,4 +490,29 @@ describe('stringify static html', () => {
|
|||
expect(ast.cached).toMatchObject([cachedArrayBailedMatcher()])
|
||||
expect(code).toMatchSnapshot()
|
||||
})
|
||||
|
||||
test('should bail for <option> elements with null values', () => {
|
||||
const { ast, code } = compileWithStringify(
|
||||
`<div><select><option :value="null" />${repeat(
|
||||
`<option value="1" />`,
|
||||
StringifyThresholds.ELEMENT_WITH_BINDING_COUNT,
|
||||
)}</select></div>`,
|
||||
)
|
||||
expect(ast.cached).toMatchObject([cachedArrayBailedMatcher()])
|
||||
expect(code).toMatchSnapshot()
|
||||
})
|
||||
|
||||
test('eligible content (elements > 20) + non-eligible content', () => {
|
||||
const { code } = compileWithStringify(
|
||||
`<div>${repeat(
|
||||
`<span/>`,
|
||||
StringifyThresholds.NODE_COUNT,
|
||||
)}<div key="1">1</div>${repeat(
|
||||
`<span/>`,
|
||||
StringifyThresholds.NODE_COUNT,
|
||||
)}</div>`,
|
||||
)
|
||||
|
||||
expect(code).toMatchSnapshot()
|
||||
})
|
||||
})
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@vue/compiler-dom",
|
||||
"version": "3.5.0-beta.1",
|
||||
"version": "3.5.13",
|
||||
"description": "@vue/compiler-dom",
|
||||
"main": "index.js",
|
||||
"module": "dist/compiler-dom.esm-bundler.js",
|
||||
|
|
|
@ -8,6 +8,7 @@ export const parserOptions: ParserOptions = {
|
|||
isVoidTag,
|
||||
isNativeTag: tag => isHTMLTag(tag) || isSVGTag(tag) || isMathMLTag(tag),
|
||||
isPreTag: tag => tag === 'pre',
|
||||
isIgnoreNewlineTag: tag => tag === 'pre' || tag === 'textarea',
|
||||
decodeEntities: __BROWSER__ ? decodeHtmlBrowser : undefined,
|
||||
|
||||
isBuiltInComponent: tag => {
|
||||
|
|
|
@ -16,8 +16,6 @@ import {
|
|||
type TemplateChildNode,
|
||||
type TextCallNode,
|
||||
type TransformContext,
|
||||
type VNodeCall,
|
||||
createArrayExpression,
|
||||
createCallExpression,
|
||||
isStaticArgOf,
|
||||
} from '@vue/compiler-core'
|
||||
|
@ -26,6 +24,7 @@ import {
|
|||
isArray,
|
||||
isBooleanAttr,
|
||||
isKnownHtmlAttr,
|
||||
isKnownMathMLAttr,
|
||||
isKnownSvgAttr,
|
||||
isString,
|
||||
isSymbol,
|
||||
|
@ -106,15 +105,23 @@ export const stringifyStatic: HoistTransform = (children, context, parent) => {
|
|||
String(currentChunk.length),
|
||||
])
|
||||
|
||||
const deleteCount = currentChunk.length - 1
|
||||
|
||||
if (isParentCached) {
|
||||
;((parent.codegenNode as VNodeCall).children as CacheExpression).value =
|
||||
createArrayExpression([staticCall])
|
||||
// if the parent is cached, then `children` is also the value of the
|
||||
// CacheExpression. Just replace the corresponding range in the cached
|
||||
// list with staticCall.
|
||||
children.splice(
|
||||
currentIndex - currentChunk.length,
|
||||
currentChunk.length,
|
||||
// @ts-expect-error
|
||||
staticCall,
|
||||
)
|
||||
} else {
|
||||
// replace the first node's hoisted expression with the static vnode call
|
||||
;(currentChunk[0].codegenNode as CacheExpression).value = staticCall
|
||||
if (currentChunk.length > 1) {
|
||||
// remove merged nodes from children
|
||||
const deleteCount = currentChunk.length - 1
|
||||
children.splice(currentIndex - currentChunk.length + 1, deleteCount)
|
||||
// also adjust index for the remaining cache items
|
||||
const cacheIndex = context.cached.indexOf(
|
||||
|
@ -128,9 +135,9 @@ export const stringifyStatic: HoistTransform = (children, context, parent) => {
|
|||
}
|
||||
context.cached.splice(cacheIndex - deleteCount + 1, deleteCount)
|
||||
}
|
||||
return deleteCount
|
||||
}
|
||||
}
|
||||
return deleteCount
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
@ -184,11 +191,13 @@ const isStringifiableAttr = (name: string, ns: Namespaces) => {
|
|||
? isKnownHtmlAttr(name)
|
||||
: ns === Namespaces.SVG
|
||||
? isKnownSvgAttr(name)
|
||||
: false) || dataAriaRE.test(name)
|
||||
: ns === Namespaces.MATH_ML
|
||||
? isKnownMathMLAttr(name)
|
||||
: false) || dataAriaRE.test(name)
|
||||
)
|
||||
}
|
||||
|
||||
const isNonStringifiable = /*#__PURE__*/ makeMap(
|
||||
const isNonStringifiable = /*@__PURE__*/ makeMap(
|
||||
`caption,thead,tr,th,tbody,td,tfoot,colgroup,col`,
|
||||
)
|
||||
|
||||
|
@ -252,8 +261,7 @@ function analyzeNode(node: StringifiableNode): [number, number] | false {
|
|||
isOptionTag &&
|
||||
isStaticArgOf(p.arg, 'value') &&
|
||||
p.exp &&
|
||||
p.exp.ast &&
|
||||
p.exp.ast.type !== 'StringLiteral'
|
||||
!p.exp.isStatic
|
||||
) {
|
||||
return bail()
|
||||
}
|
||||
|
|
|
@ -17,8 +17,8 @@ import {
|
|||
import { V_ON_WITH_KEYS, V_ON_WITH_MODIFIERS } from '../runtimeHelpers'
|
||||
import { capitalize, makeMap } from '@vue/shared'
|
||||
|
||||
const isEventOptionModifier = /*#__PURE__*/ makeMap(`passive,once,capture`)
|
||||
const isNonKeyModifier = /*#__PURE__*/ makeMap(
|
||||
const isEventOptionModifier = /*@__PURE__*/ makeMap(`passive,once,capture`)
|
||||
const isNonKeyModifier = /*@__PURE__*/ makeMap(
|
||||
// event propagation management
|
||||
`stop,prevent,self,` +
|
||||
// system modifiers + exact
|
||||
|
@ -27,15 +27,12 @@ const isNonKeyModifier = /*#__PURE__*/ makeMap(
|
|||
`middle`,
|
||||
)
|
||||
// left & right could be mouse or key modifiers based on event type
|
||||
const maybeKeyModifier = /*#__PURE__*/ makeMap('left,right')
|
||||
const isKeyboardEvent = /*#__PURE__*/ makeMap(
|
||||
`onkeyup,onkeydown,onkeypress`,
|
||||
true,
|
||||
)
|
||||
const maybeKeyModifier = /*@__PURE__*/ makeMap('left,right')
|
||||
const isKeyboardEvent = /*@__PURE__*/ makeMap(`onkeyup,onkeydown,onkeypress`)
|
||||
|
||||
const resolveModifiers = (
|
||||
key: ExpressionNode,
|
||||
modifiers: string[],
|
||||
modifiers: SimpleExpressionNode[],
|
||||
context: TransformContext,
|
||||
loc: SourceLocation,
|
||||
) => {
|
||||
|
@ -44,7 +41,7 @@ const resolveModifiers = (
|
|||
const eventOptionModifiers = []
|
||||
|
||||
for (let i = 0; i < modifiers.length; i++) {
|
||||
const modifier = modifiers[i]
|
||||
const modifier = modifiers[i].content
|
||||
|
||||
if (
|
||||
__COMPAT__ &&
|
||||
|
@ -64,7 +61,9 @@ const resolveModifiers = (
|
|||
// runtimeModifiers: modifiers that needs runtime guards
|
||||
if (maybeKeyModifier(modifier)) {
|
||||
if (isStaticExp(key)) {
|
||||
if (isKeyboardEvent((key as SimpleExpressionNode).content)) {
|
||||
if (
|
||||
isKeyboardEvent((key as SimpleExpressionNode).content.toLowerCase())
|
||||
) {
|
||||
keyModifiers.push(modifier)
|
||||
} else {
|
||||
nonKeyModifiers.push(modifier)
|
||||
|
@ -133,7 +132,7 @@ export const transformOn: DirectiveTransform = (dir, node, context) => {
|
|||
if (
|
||||
keyModifiers.length &&
|
||||
// if event name is dynamic, always wrap with keys guard
|
||||
(!isStaticExp(key) || isKeyboardEvent(key.content))
|
||||
(!isStaticExp(key) || isKeyboardEvent(key.content.toLowerCase()))
|
||||
) {
|
||||
handlerExp = createCallExpression(context.helper(V_ON_WITH_KEYS), [
|
||||
handlerExp,
|
||||
|
|
|
@ -20,7 +20,7 @@ exports[`SFC analyze <script> bindings > auto name inference > do not overwrite
|
|||
name: 'Baz'
|
||||
})
|
||||
|
||||
export default /*#__PURE__*/Object.assign(__default__, {
|
||||
export default /*@__PURE__*/Object.assign(__default__, {
|
||||
setup(__props, { expose: __expose }) {
|
||||
__expose();
|
||||
const a = 1
|
||||
|
@ -36,7 +36,7 @@ exports[`SFC analyze <script> bindings > auto name inference > do not overwrite
|
|||
name: 'Baz'
|
||||
}
|
||||
|
||||
export default /*#__PURE__*/Object.assign(__default__, {
|
||||
export default /*@__PURE__*/Object.assign(__default__, {
|
||||
setup(__props, { expose: __expose }) {
|
||||
__expose();
|
||||
const a = 1
|
||||
|
@ -53,7 +53,7 @@ exports[`SFC compile <script setup> > <script> and <script setup> co-usage > exp
|
|||
}
|
||||
const __default__ = fn();
|
||||
|
||||
export default /*#__PURE__*/Object.assign(__default__, {
|
||||
export default /*@__PURE__*/Object.assign(__default__, {
|
||||
setup(__props, { expose: __expose }) {
|
||||
__expose();
|
||||
|
||||
|
@ -91,7 +91,7 @@ exports[`SFC compile <script setup> > <script> and <script setup> co-usage > scr
|
|||
|
||||
const __default__ = {}
|
||||
|
||||
export default /*#__PURE__*/Object.assign(__default__, {
|
||||
export default /*@__PURE__*/Object.assign(__default__, {
|
||||
setup(__props, { expose: __expose }) {
|
||||
__expose();
|
||||
|
||||
|
@ -110,7 +110,7 @@ exports[`SFC compile <script setup> > <script> and <script setup> co-usage > scr
|
|||
const __default__ = {}
|
||||
|
||||
|
||||
export default /*#__PURE__*/Object.assign(__default__, {
|
||||
export default /*@__PURE__*/Object.assign(__default__, {
|
||||
setup(__props, { expose: __expose }) {
|
||||
__expose();
|
||||
|
||||
|
@ -131,7 +131,7 @@ import { x } from './x'
|
|||
}
|
||||
|
||||
|
||||
export default /*#__PURE__*/_defineComponent({
|
||||
export default /*@__PURE__*/_defineComponent({
|
||||
...__default__,
|
||||
setup(__props, { expose: __expose }) {
|
||||
__expose();
|
||||
|
@ -154,7 +154,7 @@ exports[`SFC compile <script setup> > <script> and <script setup> co-usage > scr
|
|||
const __default__ = def
|
||||
|
||||
|
||||
export default /*#__PURE__*/Object.assign(__default__, {
|
||||
export default /*@__PURE__*/Object.assign(__default__, {
|
||||
setup(__props, { expose: __expose }) {
|
||||
__expose();
|
||||
|
||||
|
@ -174,7 +174,7 @@ exports[`SFC compile <script setup> > <script> and <script setup> co-usage > spa
|
|||
some:'option'
|
||||
}
|
||||
|
||||
export default /*#__PURE__*/Object.assign(__default__, {
|
||||
export default /*@__PURE__*/Object.assign(__default__, {
|
||||
setup(__props, { expose: __expose }) {
|
||||
__expose();
|
||||
|
||||
|
@ -194,7 +194,7 @@ exports[`SFC compile <script setup> > <script> and <script setup> co-usage > spa
|
|||
some:'option'
|
||||
}
|
||||
|
||||
export default /*#__PURE__*/Object.assign(__default__, {
|
||||
export default /*@__PURE__*/Object.assign(__default__, {
|
||||
setup(__props, { expose: __expose }) {
|
||||
__expose();
|
||||
|
||||
|
@ -880,11 +880,13 @@ export default {
|
|||
|
||||
const count = ref(0)
|
||||
const style = { color: 'red' }
|
||||
const height = ref(0)
|
||||
|
||||
return (_ctx, _push, _parent, _attrs) => {
|
||||
const _cssVars = { style: {
|
||||
"--xxxxxxxx-count": (count.value),
|
||||
"--xxxxxxxx-style\\\\.color": (style.color)
|
||||
"--xxxxxxxx-style\\\\.color": (style.color),
|
||||
"--xxxxxxxx-height\\\\ \\\\+\\\\ \\\\\\"px\\\\\\"": (height.value + "px")
|
||||
}}
|
||||
_push(\`<!--[--><div\${
|
||||
_ssrRenderAttrs(_cssVars)
|
||||
|
@ -1082,6 +1084,29 @@ return (_ctx, _cache) => {
|
|||
}"
|
||||
`;
|
||||
|
||||
exports[`SFC compile <script setup> > inlineTemplate mode > v-model w/ newlines codegen 1`] = `
|
||||
"import { unref as _unref, isRef as _isRef, vModelText as _vModelText, withDirectives as _withDirectives, openBlock as _openBlock, createElementBlock as _createElementBlock } from "vue"
|
||||
|
||||
|
||||
export default {
|
||||
setup(__props) {
|
||||
|
||||
const count = ref(0)
|
||||
|
||||
return (_ctx, _cache) => {
|
||||
return _withDirectives((_openBlock(), _createElementBlock("input", {
|
||||
"onUpdate:modelValue": _cache[0] || (_cache[0] = $event => (_isRef(count) ? (count).value = $event : null))
|
||||
}, null, 512 /* NEED_PATCH */)), [
|
||||
[_vModelText,
|
||||
_unref(count)
|
||||
]
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`SFC compile <script setup> > inlineTemplate mode > with defineExpose() 1`] = `
|
||||
"
|
||||
export default {
|
||||
|
@ -1140,7 +1165,7 @@ exports[`SFC compile <script setup> > with TypeScript > const Enum 1`] = `
|
|||
"import { defineComponent as _defineComponent } from 'vue'
|
||||
const enum Foo { A = 123 }
|
||||
|
||||
export default /*#__PURE__*/_defineComponent({
|
||||
export default /*@__PURE__*/_defineComponent({
|
||||
setup(__props, { expose: __expose }) {
|
||||
__expose();
|
||||
|
||||
|
@ -1156,7 +1181,7 @@ exports[`SFC compile <script setup> > with TypeScript > hoist type declarations
|
|||
export interface Foo {}
|
||||
type Bar = {}
|
||||
|
||||
export default /*#__PURE__*/_defineComponent({
|
||||
export default /*@__PURE__*/_defineComponent({
|
||||
setup(__props, { expose: __expose }) {
|
||||
__expose();
|
||||
|
||||
|
@ -1172,7 +1197,7 @@ exports[`SFC compile <script setup> > with TypeScript > import type 1`] = `
|
|||
import type { Foo } from './main.ts'
|
||||
import { type Bar, Baz } from './main.ts'
|
||||
|
||||
export default /*#__PURE__*/_defineComponent({
|
||||
export default /*@__PURE__*/_defineComponent({
|
||||
setup(__props, { expose: __expose }) {
|
||||
__expose();
|
||||
|
||||
|
@ -1187,7 +1212,7 @@ exports[`SFC compile <script setup> > with TypeScript > runtime Enum 1`] = `
|
|||
"import { defineComponent as _defineComponent } from 'vue'
|
||||
enum Foo { A = 123 }
|
||||
|
||||
export default /*#__PURE__*/_defineComponent({
|
||||
export default /*@__PURE__*/_defineComponent({
|
||||
setup(__props, { expose: __expose }) {
|
||||
__expose();
|
||||
|
||||
|
@ -1205,7 +1230,7 @@ exports[`SFC compile <script setup> > with TypeScript > runtime Enum in normal s
|
|||
const enum C { C = "C" }
|
||||
enum B { B = "B" }
|
||||
|
||||
export default /*#__PURE__*/_defineComponent({
|
||||
export default /*@__PURE__*/_defineComponent({
|
||||
setup(__props, { expose: __expose }) {
|
||||
__expose();
|
||||
|
||||
|
@ -1221,7 +1246,7 @@ exports[`SFC compile <script setup> > with TypeScript > with generic attribute 1
|
|||
"import { defineComponent as _defineComponent } from 'vue'
|
||||
type Bar = {}
|
||||
|
||||
export default /*#__PURE__*/_defineComponent({
|
||||
export default /*@__PURE__*/_defineComponent({
|
||||
setup(__props, { expose: __expose }) {
|
||||
__expose();
|
||||
|
||||
|
@ -1250,7 +1275,7 @@ exports[`SFC genDefaultAs > <script setup> only w/ ts 1`] = `
|
|||
"import { defineComponent as _defineComponent } from 'vue'
|
||||
const a = 1
|
||||
|
||||
const _sfc_ = /*#__PURE__*/_defineComponent({
|
||||
const _sfc_ = /*@__PURE__*/_defineComponent({
|
||||
setup(__props, { expose: __expose }) {
|
||||
__expose();
|
||||
|
||||
|
@ -1265,7 +1290,7 @@ exports[`SFC genDefaultAs > <script> + <script setup> 1`] = `
|
|||
"
|
||||
const __default__ = {}
|
||||
|
||||
const _sfc_ = /*#__PURE__*/Object.assign(__default__, {
|
||||
const _sfc_ = /*@__PURE__*/Object.assign(__default__, {
|
||||
setup(__props, { expose: __expose }) {
|
||||
__expose();
|
||||
|
||||
|
@ -1281,7 +1306,7 @@ exports[`SFC genDefaultAs > <script> + <script setup> 2`] = `
|
|||
"
|
||||
const __default__ = {}
|
||||
|
||||
const _sfc_ = /*#__PURE__*/Object.assign(__default__, {
|
||||
const _sfc_ = /*@__PURE__*/Object.assign(__default__, {
|
||||
setup(__props, { expose: __expose }) {
|
||||
__expose();
|
||||
|
||||
|
@ -1298,7 +1323,7 @@ exports[`SFC genDefaultAs > <script> + <script setup> w/ ts 1`] = `
|
|||
|
||||
const __default__ = {}
|
||||
|
||||
const _sfc_ = /*#__PURE__*/_defineComponent({
|
||||
const _sfc_ = /*@__PURE__*/_defineComponent({
|
||||
...__default__,
|
||||
setup(__props, { expose: __expose }) {
|
||||
__expose();
|
||||
|
|
|
@ -472,6 +472,23 @@ describe('SFC compile <script setup>', () => {
|
|||
assertCode(content)
|
||||
})
|
||||
|
||||
test('v-model w/ newlines codegen', () => {
|
||||
const { content } = compile(
|
||||
`<script setup>
|
||||
const count = ref(0)
|
||||
</script>
|
||||
<template>
|
||||
<input v-model="
|
||||
count
|
||||
">
|
||||
</template>
|
||||
`,
|
||||
{ inlineTemplate: true },
|
||||
)
|
||||
expect(content).toMatch(`_isRef(count) ? (count).value = $event : null`)
|
||||
assertCode(content)
|
||||
})
|
||||
|
||||
test('v-model should not generate ref assignment code for non-setup bindings', () => {
|
||||
const { content } = compile(
|
||||
`<script setup>
|
||||
|
@ -606,6 +623,7 @@ describe('SFC compile <script setup>', () => {
|
|||
import { ref } from 'vue'
|
||||
const count = ref(0)
|
||||
const style = { color: 'red' }
|
||||
const height = ref(0)
|
||||
</script>
|
||||
<template>
|
||||
<div>{{ count }}</div>
|
||||
|
@ -614,6 +632,7 @@ describe('SFC compile <script setup>', () => {
|
|||
<style>
|
||||
div { color: v-bind(count) }
|
||||
span { color: v-bind(style.color) }
|
||||
span { color: v-bind(height + "px") }
|
||||
</style>
|
||||
`,
|
||||
{
|
||||
|
@ -629,6 +648,9 @@ describe('SFC compile <script setup>', () => {
|
|||
expect(content).not.toMatch(`useCssVars`)
|
||||
expect(content).toMatch(`"--${mockId}-count": (count.value)`)
|
||||
expect(content).toMatch(`"--${mockId}-style\\\\.color": (style.color)`)
|
||||
expect(content).toMatch(
|
||||
`"--${mockId}-height\\\\ \\\\+\\\\ \\\\\\"px\\\\\\"": (height.value + "px")`,
|
||||
)
|
||||
assertCode(content)
|
||||
})
|
||||
|
||||
|
@ -1352,7 +1374,7 @@ describe('SFC genDefaultAs', () => {
|
|||
)
|
||||
expect(content).not.toMatch('export default')
|
||||
expect(content).toMatch(
|
||||
`const _sfc_ = /*#__PURE__*/Object.assign(__default__`,
|
||||
`const _sfc_ = /*@__PURE__*/Object.assign(__default__`,
|
||||
)
|
||||
assertCode(content)
|
||||
})
|
||||
|
@ -1371,7 +1393,7 @@ describe('SFC genDefaultAs', () => {
|
|||
)
|
||||
expect(content).not.toMatch('export default')
|
||||
expect(content).toMatch(
|
||||
`const _sfc_ = /*#__PURE__*/Object.assign(__default__`,
|
||||
`const _sfc_ = /*@__PURE__*/Object.assign(__default__`,
|
||||
)
|
||||
assertCode(content)
|
||||
})
|
||||
|
@ -1400,7 +1422,7 @@ describe('SFC genDefaultAs', () => {
|
|||
},
|
||||
)
|
||||
expect(content).not.toMatch('export default')
|
||||
expect(content).toMatch(`const _sfc_ = /*#__PURE__*/_defineComponent(`)
|
||||
expect(content).toMatch(`const _sfc_ = /*@__PURE__*/_defineComponent(`)
|
||||
assertCode(content)
|
||||
})
|
||||
|
||||
|
@ -1418,7 +1440,7 @@ describe('SFC genDefaultAs', () => {
|
|||
)
|
||||
expect(content).not.toMatch('export default')
|
||||
expect(content).toMatch(
|
||||
`const _sfc_ = /*#__PURE__*/_defineComponent({\n ...__default__`,
|
||||
`const _sfc_ = /*@__PURE__*/_defineComponent({\n ...__default__`,
|
||||
)
|
||||
assertCode(content)
|
||||
})
|
||||
|
|
|
@ -18,7 +18,7 @@ return { myEmit }
|
|||
exports[`defineEmits > w/ runtime options 1`] = `
|
||||
"import { defineComponent as _defineComponent } from 'vue'
|
||||
|
||||
export default /*#__PURE__*/_defineComponent({
|
||||
export default /*@__PURE__*/_defineComponent({
|
||||
emits: ['a', 'b'],
|
||||
setup(__props, { expose: __expose, emit: __emit }) {
|
||||
__expose();
|
||||
|
@ -35,7 +35,7 @@ exports[`defineEmits > w/ type (exported interface) 1`] = `
|
|||
"import { defineComponent as _defineComponent } from 'vue'
|
||||
export interface Emits { (e: 'foo' | 'bar'): void }
|
||||
|
||||
export default /*#__PURE__*/_defineComponent({
|
||||
export default /*@__PURE__*/_defineComponent({
|
||||
emits: ["foo", "bar"],
|
||||
setup(__props, { expose: __expose, emit: __emit }) {
|
||||
__expose();
|
||||
|
@ -52,7 +52,7 @@ exports[`defineEmits > w/ type (exported type alias) 1`] = `
|
|||
"import { defineComponent as _defineComponent } from 'vue'
|
||||
export type Emits = { (e: 'foo' | 'bar'): void }
|
||||
|
||||
export default /*#__PURE__*/_defineComponent({
|
||||
export default /*@__PURE__*/_defineComponent({
|
||||
emits: ["foo", "bar"],
|
||||
setup(__props, { expose: __expose, emit: __emit }) {
|
||||
__expose();
|
||||
|
@ -69,7 +69,7 @@ exports[`defineEmits > w/ type (interface ts type) 1`] = `
|
|||
"import { defineComponent as _defineComponent } from 'vue'
|
||||
interface Emits { (e: 'foo'): void }
|
||||
|
||||
export default /*#__PURE__*/_defineComponent({
|
||||
export default /*@__PURE__*/_defineComponent({
|
||||
emits: ['foo'],
|
||||
setup(__props, { expose: __expose, emit: __emit }) {
|
||||
__expose();
|
||||
|
@ -87,7 +87,7 @@ exports[`defineEmits > w/ type (interface w/ extends) 1`] = `
|
|||
interface Base { (e: 'foo'): void }
|
||||
interface Emits extends Base { (e: 'bar'): void }
|
||||
|
||||
export default /*#__PURE__*/_defineComponent({
|
||||
export default /*@__PURE__*/_defineComponent({
|
||||
emits: ["bar", "foo"],
|
||||
setup(__props, { expose: __expose, emit: __emit }) {
|
||||
__expose();
|
||||
|
@ -104,7 +104,7 @@ exports[`defineEmits > w/ type (interface) 1`] = `
|
|||
"import { defineComponent as _defineComponent } from 'vue'
|
||||
interface Emits { (e: 'foo' | 'bar'): void }
|
||||
|
||||
export default /*#__PURE__*/_defineComponent({
|
||||
export default /*@__PURE__*/_defineComponent({
|
||||
emits: ["foo", "bar"],
|
||||
setup(__props, { expose: __expose, emit: __emit }) {
|
||||
__expose();
|
||||
|
@ -120,7 +120,7 @@ return { emit }
|
|||
exports[`defineEmits > w/ type (property syntax string literal) 1`] = `
|
||||
"import { defineComponent as _defineComponent } from 'vue'
|
||||
|
||||
export default /*#__PURE__*/_defineComponent({
|
||||
export default /*@__PURE__*/_defineComponent({
|
||||
emits: ["foo:bar"],
|
||||
setup(__props, { expose: __expose, emit: __emit }) {
|
||||
__expose();
|
||||
|
@ -136,7 +136,7 @@ return { emit }
|
|||
exports[`defineEmits > w/ type (property syntax) 1`] = `
|
||||
"import { defineComponent as _defineComponent } from 'vue'
|
||||
|
||||
export default /*#__PURE__*/_defineComponent({
|
||||
export default /*@__PURE__*/_defineComponent({
|
||||
emits: ["foo", "bar"],
|
||||
setup(__props, { expose: __expose, emit: __emit }) {
|
||||
__expose();
|
||||
|
@ -153,7 +153,7 @@ exports[`defineEmits > w/ type (referenced exported function type) 1`] = `
|
|||
"import { defineComponent as _defineComponent } from 'vue'
|
||||
export type Emits = (e: 'foo' | 'bar') => void
|
||||
|
||||
export default /*#__PURE__*/_defineComponent({
|
||||
export default /*@__PURE__*/_defineComponent({
|
||||
emits: ["foo", "bar"],
|
||||
setup(__props, { expose: __expose, emit: __emit }) {
|
||||
__expose();
|
||||
|
@ -170,7 +170,7 @@ exports[`defineEmits > w/ type (referenced function type) 1`] = `
|
|||
"import { defineComponent as _defineComponent } from 'vue'
|
||||
type Emits = (e: 'foo' | 'bar') => void
|
||||
|
||||
export default /*#__PURE__*/_defineComponent({
|
||||
export default /*@__PURE__*/_defineComponent({
|
||||
emits: ["foo", "bar"],
|
||||
setup(__props, { expose: __expose, emit: __emit }) {
|
||||
__expose();
|
||||
|
@ -187,7 +187,7 @@ exports[`defineEmits > w/ type (type alias) 1`] = `
|
|||
"import { defineComponent as _defineComponent } from 'vue'
|
||||
type Emits = { (e: 'foo' | 'bar'): void }
|
||||
|
||||
export default /*#__PURE__*/_defineComponent({
|
||||
export default /*@__PURE__*/_defineComponent({
|
||||
emits: ["foo", "bar"],
|
||||
setup(__props, { expose: __expose, emit: __emit }) {
|
||||
__expose();
|
||||
|
@ -203,7 +203,7 @@ return { emit }
|
|||
exports[`defineEmits > w/ type (type literal w/ call signatures) 1`] = `
|
||||
"import { defineComponent as _defineComponent } from 'vue'
|
||||
|
||||
export default /*#__PURE__*/_defineComponent({
|
||||
export default /*@__PURE__*/_defineComponent({
|
||||
emits: ["foo", "bar", "baz"],
|
||||
setup(__props, { expose: __expose, emit: __emit }) {
|
||||
__expose();
|
||||
|
@ -221,7 +221,7 @@ exports[`defineEmits > w/ type (type references in union) 1`] = `
|
|||
type BaseEmit = "change"
|
||||
type Emit = "some" | "emit" | BaseEmit
|
||||
|
||||
export default /*#__PURE__*/_defineComponent({
|
||||
export default /*@__PURE__*/_defineComponent({
|
||||
emits: ["some", "emit", "change", "another"],
|
||||
setup(__props, { expose: __expose, emit: __emit }) {
|
||||
__expose();
|
||||
|
@ -237,7 +237,7 @@ return { emit }
|
|||
exports[`defineEmits > w/ type (union) 1`] = `
|
||||
"import { defineComponent as _defineComponent } from 'vue'
|
||||
|
||||
export default /*#__PURE__*/_defineComponent({
|
||||
export default /*@__PURE__*/_defineComponent({
|
||||
emits: ["foo", "bar", "baz"],
|
||||
setup(__props, { expose: __expose, emit: __emit }) {
|
||||
__expose();
|
||||
|
@ -253,7 +253,7 @@ return { emit }
|
|||
exports[`defineEmits > w/ type 1`] = `
|
||||
"import { defineComponent as _defineComponent } from 'vue'
|
||||
|
||||
export default /*#__PURE__*/_defineComponent({
|
||||
export default /*@__PURE__*/_defineComponent({
|
||||
emits: ["foo", "bar"],
|
||||
setup(__props, { expose: __expose, emit: __emit }) {
|
||||
__expose();
|
||||
|
@ -271,7 +271,7 @@ exports[`defineEmits > w/ type from normal script 1`] = `
|
|||
|
||||
export interface Emits { (e: 'foo' | 'bar'): void }
|
||||
|
||||
export default /*#__PURE__*/_defineComponent({
|
||||
export default /*@__PURE__*/_defineComponent({
|
||||
emits: ["foo", "bar"],
|
||||
setup(__props, { expose: __expose, emit: __emit }) {
|
||||
__expose();
|
||||
|
|
|
@ -29,7 +29,7 @@ return { modelValue, c, toString }
|
|||
exports[`defineModel() > get / set transformers 1`] = `
|
||||
"import { useModel as _useModel, defineComponent as _defineComponent } from 'vue'
|
||||
|
||||
export default /*#__PURE__*/_defineComponent({
|
||||
export default /*@__PURE__*/_defineComponent({
|
||||
props: {
|
||||
"modelValue": {
|
||||
required: true
|
||||
|
@ -54,7 +54,7 @@ return { modelValue }
|
|||
exports[`defineModel() > get / set transformers 2`] = `
|
||||
"import { useModel as _useModel, defineComponent as _defineComponent } from 'vue'
|
||||
|
||||
export default /*#__PURE__*/_defineComponent({
|
||||
export default /*@__PURE__*/_defineComponent({
|
||||
props: {
|
||||
"modelValue": {
|
||||
default: 0,
|
||||
|
@ -80,8 +80,8 @@ return { modelValue }
|
|||
exports[`defineModel() > usage w/ props destructure 1`] = `
|
||||
"import { useModel as _useModel, mergeModels as _mergeModels, defineComponent as _defineComponent } from 'vue'
|
||||
|
||||
export default /*#__PURE__*/_defineComponent({
|
||||
props: /*#__PURE__*/_mergeModels({
|
||||
export default /*@__PURE__*/_defineComponent({
|
||||
props: /*@__PURE__*/_mergeModels({
|
||||
x: { type: Number, required: true }
|
||||
}, {
|
||||
"modelValue": {
|
||||
|
@ -106,7 +106,7 @@ return { modelValue }
|
|||
exports[`defineModel() > w/ Boolean And Function types, production mode 1`] = `
|
||||
"import { useModel as _useModel, defineComponent as _defineComponent } from 'vue'
|
||||
|
||||
export default /*#__PURE__*/_defineComponent({
|
||||
export default /*@__PURE__*/_defineComponent({
|
||||
props: {
|
||||
"modelValue": { type: [Boolean, String] },
|
||||
"modelModifiers": {},
|
||||
|
@ -127,7 +127,7 @@ exports[`defineModel() > w/ array props 1`] = `
|
|||
"import { useModel as _useModel, mergeModels as _mergeModels } from 'vue'
|
||||
|
||||
export default {
|
||||
props: /*#__PURE__*/_mergeModels(['foo', 'bar'], {
|
||||
props: /*@__PURE__*/_mergeModels(['foo', 'bar'], {
|
||||
"count": {},
|
||||
"countModifiers": {},
|
||||
}),
|
||||
|
@ -148,11 +148,11 @@ exports[`defineModel() > w/ defineProps and defineEmits 1`] = `
|
|||
"import { useModel as _useModel, mergeModels as _mergeModels } from 'vue'
|
||||
|
||||
export default {
|
||||
props: /*#__PURE__*/_mergeModels({ foo: String }, {
|
||||
props: /*@__PURE__*/_mergeModels({ foo: String }, {
|
||||
"modelValue": { default: 0 },
|
||||
"modelModifiers": {},
|
||||
}),
|
||||
emits: /*#__PURE__*/_mergeModels(['change'], ["update:modelValue"]),
|
||||
emits: /*@__PURE__*/_mergeModels(['change'], ["update:modelValue"]),
|
||||
setup(__props, { expose: __expose }) {
|
||||
__expose();
|
||||
|
||||
|
@ -169,7 +169,7 @@ return { count }
|
|||
exports[`defineModel() > w/ types, basic usage 1`] = `
|
||||
"import { useModel as _useModel, defineComponent as _defineComponent } from 'vue'
|
||||
|
||||
export default /*#__PURE__*/_defineComponent({
|
||||
export default /*@__PURE__*/_defineComponent({
|
||||
props: {
|
||||
"modelValue": { type: [Boolean, String] },
|
||||
"modelModifiers": {},
|
||||
|
@ -198,7 +198,7 @@ return { modelValue, count, disabled, any }
|
|||
exports[`defineModel() > w/ types, production mode 1`] = `
|
||||
"import { useModel as _useModel, defineComponent as _defineComponent } from 'vue'
|
||||
|
||||
export default /*#__PURE__*/_defineComponent({
|
||||
export default /*@__PURE__*/_defineComponent({
|
||||
props: {
|
||||
"modelValue": { type: Boolean },
|
||||
"modelModifiers": {},
|
||||
|
@ -230,7 +230,7 @@ return { modelValue, fn, fnWithDefault, str, optional }
|
|||
exports[`defineModel() > w/ types, production mode, boolean + multiple types 1`] = `
|
||||
"import { useModel as _useModel, defineComponent as _defineComponent } from 'vue'
|
||||
|
||||
export default /*#__PURE__*/_defineComponent({
|
||||
export default /*@__PURE__*/_defineComponent({
|
||||
props: {
|
||||
"modelValue": { type: [Boolean, String, Object] },
|
||||
"modelModifiers": {},
|
||||
|
@ -250,7 +250,7 @@ return { modelValue }
|
|||
exports[`defineModel() > w/ types, production mode, function + runtime opts + multiple types 1`] = `
|
||||
"import { useModel as _useModel, defineComponent as _defineComponent } from 'vue'
|
||||
|
||||
export default /*#__PURE__*/_defineComponent({
|
||||
export default /*@__PURE__*/_defineComponent({
|
||||
props: {
|
||||
"modelValue": { type: [Number, Function], ...{ default: () => 1 } },
|
||||
"modelModifiers": {},
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
exports[`defineOptions() > basic usage 1`] = `
|
||||
"
|
||||
export default /*#__PURE__*/Object.assign({ name: 'FooApp' }, {
|
||||
export default /*@__PURE__*/Object.assign({ name: 'FooApp' }, {
|
||||
setup(__props, { expose: __expose }) {
|
||||
__expose();
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@ interface Props {
|
|||
foo?: number;
|
||||
}
|
||||
|
||||
export default /*#__PURE__*/_defineComponent({
|
||||
export default /*@__PURE__*/_defineComponent({
|
||||
__name: 'app.ce',
|
||||
props: {
|
||||
foo: { default: 5.5, type: Number }
|
||||
|
@ -43,7 +43,7 @@ return { props }
|
|||
exports[`defineProps > custom element retains the props type & production mode 1`] = `
|
||||
"import { defineComponent as _defineComponent } from 'vue'
|
||||
|
||||
export default /*#__PURE__*/_defineComponent({
|
||||
export default /*@__PURE__*/_defineComponent({
|
||||
__name: 'app.ce',
|
||||
props: {
|
||||
foo: {type: Number}
|
||||
|
@ -62,7 +62,7 @@ return { props }
|
|||
exports[`defineProps > defineProps w/ runtime options 1`] = `
|
||||
"import { defineComponent as _defineComponent } from 'vue'
|
||||
|
||||
export default /*#__PURE__*/_defineComponent({
|
||||
export default /*@__PURE__*/_defineComponent({
|
||||
props: { foo: String },
|
||||
setup(__props, { expose: __expose }) {
|
||||
__expose();
|
||||
|
@ -78,7 +78,7 @@ return { props }
|
|||
exports[`defineProps > destructure without enabling reactive destructure 1`] = `
|
||||
"import { defineComponent as _defineComponent } from 'vue'
|
||||
|
||||
export default /*#__PURE__*/_defineComponent({
|
||||
export default /*@__PURE__*/_defineComponent({
|
||||
props: {
|
||||
foo: { type: null, required: true }
|
||||
},
|
||||
|
@ -96,7 +96,7 @@ return { foo }
|
|||
exports[`defineProps > should escape names w/ special symbols 1`] = `
|
||||
"import { defineComponent as _defineComponent } from 'vue'
|
||||
|
||||
export default /*#__PURE__*/_defineComponent({
|
||||
export default /*@__PURE__*/_defineComponent({
|
||||
props: {
|
||||
"spa ce": { type: null, required: true },
|
||||
"exclamation!mark": { type: null, required: true },
|
||||
|
@ -141,7 +141,7 @@ return { }
|
|||
exports[`defineProps > w/ TS assertion 1`] = `
|
||||
"import { defineComponent as _defineComponent } from 'vue'
|
||||
|
||||
export default /*#__PURE__*/_defineComponent({
|
||||
export default /*@__PURE__*/_defineComponent({
|
||||
props: ['foo'],
|
||||
setup(__props, { expose: __expose }) {
|
||||
__expose();
|
||||
|
@ -158,7 +158,7 @@ exports[`defineProps > w/ exported interface 1`] = `
|
|||
"import { defineComponent as _defineComponent } from 'vue'
|
||||
export interface Props { x?: number }
|
||||
|
||||
export default /*#__PURE__*/_defineComponent({
|
||||
export default /*@__PURE__*/_defineComponent({
|
||||
props: {
|
||||
x: { type: Number, required: false }
|
||||
},
|
||||
|
@ -178,7 +178,7 @@ exports[`defineProps > w/ exported interface in normal script 1`] = `
|
|||
|
||||
export interface Props { x?: number }
|
||||
|
||||
export default /*#__PURE__*/_defineComponent({
|
||||
export default /*@__PURE__*/_defineComponent({
|
||||
props: {
|
||||
x: { type: Number, required: false }
|
||||
},
|
||||
|
@ -197,7 +197,7 @@ exports[`defineProps > w/ exported type alias 1`] = `
|
|||
"import { defineComponent as _defineComponent } from 'vue'
|
||||
export type Props = { x?: number }
|
||||
|
||||
export default /*#__PURE__*/_defineComponent({
|
||||
export default /*@__PURE__*/_defineComponent({
|
||||
props: {
|
||||
x: { type: Number, required: false }
|
||||
},
|
||||
|
@ -222,7 +222,34 @@ interface Bar extends Foo { y?: number }
|
|||
|
||||
interface Foo { x?: number }
|
||||
|
||||
export default /*#__PURE__*/_defineComponent({
|
||||
export default /*@__PURE__*/_defineComponent({
|
||||
props: {
|
||||
z: { type: Number, required: true },
|
||||
y: { type: String, required: true },
|
||||
x: { type: Number, required: false }
|
||||
},
|
||||
setup(__props: any, { expose: __expose }) {
|
||||
__expose();
|
||||
|
||||
|
||||
|
||||
return { }
|
||||
}
|
||||
|
||||
})"
|
||||
`;
|
||||
|
||||
exports[`defineProps > w/ extends intersection type 1`] = `
|
||||
"import { defineComponent as _defineComponent } from 'vue'
|
||||
type Foo = {
|
||||
x?: number;
|
||||
};
|
||||
interface Props extends Foo {
|
||||
z: number
|
||||
y: string
|
||||
}
|
||||
|
||||
export default /*@__PURE__*/_defineComponent({
|
||||
props: {
|
||||
z: { type: Number, required: true },
|
||||
y: { type: String, required: true },
|
||||
|
@ -259,7 +286,7 @@ exports[`defineProps > w/ interface 1`] = `
|
|||
"import { defineComponent as _defineComponent } from 'vue'
|
||||
interface Props { x?: number }
|
||||
|
||||
export default /*#__PURE__*/_defineComponent({
|
||||
export default /*@__PURE__*/_defineComponent({
|
||||
props: {
|
||||
x: { type: Number, required: false }
|
||||
},
|
||||
|
@ -268,6 +295,31 @@ export default /*#__PURE__*/_defineComponent({
|
|||
|
||||
|
||||
|
||||
return { }
|
||||
}
|
||||
|
||||
})"
|
||||
`;
|
||||
|
||||
exports[`defineProps > w/ intersection type 1`] = `
|
||||
"import { defineComponent as _defineComponent } from 'vue'
|
||||
type Foo = {
|
||||
x?: number;
|
||||
};
|
||||
type Bar = {
|
||||
y: string;
|
||||
};
|
||||
|
||||
export default /*@__PURE__*/_defineComponent({
|
||||
props: {
|
||||
x: { type: Number, required: false },
|
||||
y: { type: String, required: true }
|
||||
},
|
||||
setup(__props: any, { expose: __expose }) {
|
||||
__expose();
|
||||
|
||||
|
||||
|
||||
return { }
|
||||
}
|
||||
|
||||
|
@ -296,7 +348,7 @@ interface Test {}
|
|||
type Alias = number[]
|
||||
|
||||
|
||||
export default /*#__PURE__*/_defineComponent({
|
||||
export default /*@__PURE__*/_defineComponent({
|
||||
props: {
|
||||
string: { type: String, required: true },
|
||||
number: { type: Number, required: true },
|
||||
|
@ -353,7 +405,7 @@ exports[`defineProps > w/ type alias 1`] = `
|
|||
"import { defineComponent as _defineComponent } from 'vue'
|
||||
type Props = { x?: number }
|
||||
|
||||
export default /*#__PURE__*/_defineComponent({
|
||||
export default /*@__PURE__*/_defineComponent({
|
||||
props: {
|
||||
x: { type: Number, required: false }
|
||||
},
|
||||
|
@ -372,8 +424,8 @@ exports[`defineProps > withDefaults (dynamic) 1`] = `
|
|||
"import { mergeDefaults as _mergeDefaults, defineComponent as _defineComponent } from 'vue'
|
||||
import { defaults } from './foo'
|
||||
|
||||
export default /*#__PURE__*/_defineComponent({
|
||||
props: /*#__PURE__*/_mergeDefaults({
|
||||
export default /*@__PURE__*/_defineComponent({
|
||||
props: /*@__PURE__*/_mergeDefaults({
|
||||
foo: { type: String, required: false },
|
||||
bar: { type: Number, required: false },
|
||||
baz: { type: Boolean, required: true }
|
||||
|
@ -393,8 +445,8 @@ exports[`defineProps > withDefaults (dynamic) w/ production mode 1`] = `
|
|||
"import { mergeDefaults as _mergeDefaults, defineComponent as _defineComponent } from 'vue'
|
||||
import { defaults } from './foo'
|
||||
|
||||
export default /*#__PURE__*/_defineComponent({
|
||||
props: /*#__PURE__*/_mergeDefaults({
|
||||
export default /*@__PURE__*/_defineComponent({
|
||||
props: /*@__PURE__*/_mergeDefaults({
|
||||
foo: { type: Function },
|
||||
bar: { type: Boolean },
|
||||
baz: { type: [Boolean, Function] },
|
||||
|
@ -415,8 +467,8 @@ exports[`defineProps > withDefaults (reference) 1`] = `
|
|||
"import { mergeDefaults as _mergeDefaults, defineComponent as _defineComponent } from 'vue'
|
||||
import { defaults } from './foo'
|
||||
|
||||
export default /*#__PURE__*/_defineComponent({
|
||||
props: /*#__PURE__*/_mergeDefaults({
|
||||
export default /*@__PURE__*/_defineComponent({
|
||||
props: /*@__PURE__*/_mergeDefaults({
|
||||
foo: { type: String, required: false },
|
||||
bar: { type: Number, required: false },
|
||||
baz: { type: Boolean, required: true }
|
||||
|
@ -439,7 +491,7 @@ exports[`defineProps > withDefaults (static) + normal script 1`] = `
|
|||
a?: string;
|
||||
}
|
||||
|
||||
export default /*#__PURE__*/_defineComponent({
|
||||
export default /*@__PURE__*/_defineComponent({
|
||||
props: {
|
||||
a: { type: String, required: false, default: "a" }
|
||||
},
|
||||
|
@ -457,7 +509,7 @@ return { props }
|
|||
exports[`defineProps > withDefaults (static) 1`] = `
|
||||
"import { defineComponent as _defineComponent } from 'vue'
|
||||
|
||||
export default /*#__PURE__*/_defineComponent({
|
||||
export default /*@__PURE__*/_defineComponent({
|
||||
props: {
|
||||
foo: { type: String, required: false, default: 'hi' },
|
||||
bar: { type: Number, required: false },
|
||||
|
@ -481,7 +533,7 @@ return { props }
|
|||
exports[`defineProps > withDefaults (static) w/ production mode 1`] = `
|
||||
"import { defineComponent as _defineComponent } from 'vue'
|
||||
|
||||
export default /*#__PURE__*/_defineComponent({
|
||||
export default /*@__PURE__*/_defineComponent({
|
||||
props: {
|
||||
foo: {},
|
||||
bar: { type: Boolean },
|
||||
|
@ -502,8 +554,8 @@ return { props }
|
|||
exports[`defineProps > withDefaults w/ dynamic object method 1`] = `
|
||||
"import { mergeDefaults as _mergeDefaults, defineComponent as _defineComponent } from 'vue'
|
||||
|
||||
export default /*#__PURE__*/_defineComponent({
|
||||
props: /*#__PURE__*/_mergeDefaults({
|
||||
export default /*@__PURE__*/_defineComponent({
|
||||
props: /*@__PURE__*/_mergeDefaults({
|
||||
foo: { type: Function, required: false }
|
||||
}, {
|
||||
['fo' + 'o']() { return 'foo' }
|
||||
|
|
|
@ -62,7 +62,7 @@ exports[`sfc reactive props destructure > default values w/ array runtime declar
|
|||
"import { mergeDefaults as _mergeDefaults } from 'vue'
|
||||
|
||||
export default {
|
||||
props: /*#__PURE__*/_mergeDefaults(['foo', 'bar', 'baz'], {
|
||||
props: /*@__PURE__*/_mergeDefaults(['foo', 'bar', 'baz'], {
|
||||
foo: 1,
|
||||
bar: () => ({}),
|
||||
func: () => {}, __skip_func: true
|
||||
|
@ -81,7 +81,7 @@ exports[`sfc reactive props destructure > default values w/ object runtime decla
|
|||
"import { mergeDefaults as _mergeDefaults } from 'vue'
|
||||
|
||||
export default {
|
||||
props: /*#__PURE__*/_mergeDefaults({ foo: Number, bar: Object, func: Function, ext: null }, {
|
||||
props: /*@__PURE__*/_mergeDefaults({ foo: Number, bar: Object, func: Function, ext: null }, {
|
||||
foo: 1,
|
||||
bar: () => ({}),
|
||||
func: () => {}, __skip_func: true,
|
||||
|
@ -101,7 +101,7 @@ exports[`sfc reactive props destructure > default values w/ runtime declaration
|
|||
"import { mergeDefaults as _mergeDefaults } from 'vue'
|
||||
|
||||
export default {
|
||||
props: /*#__PURE__*/_mergeDefaults(['foo', 'foo:bar'], {
|
||||
props: /*@__PURE__*/_mergeDefaults(['foo', 'foo:bar'], {
|
||||
foo: 1,
|
||||
"foo:bar": 'foo-bar'
|
||||
}),
|
||||
|
@ -118,7 +118,7 @@ return () => {}
|
|||
exports[`sfc reactive props destructure > default values w/ type declaration & key is string 1`] = `
|
||||
"import { defineComponent as _defineComponent } from 'vue'
|
||||
|
||||
export default /*#__PURE__*/_defineComponent({
|
||||
export default /*@__PURE__*/_defineComponent({
|
||||
props: {
|
||||
foo: { type: Number, required: true, default: 1 },
|
||||
bar: { type: Number, required: true, default: 2 },
|
||||
|
@ -138,7 +138,7 @@ return () => {}
|
|||
exports[`sfc reactive props destructure > default values w/ type declaration 1`] = `
|
||||
"import { defineComponent as _defineComponent } from 'vue'
|
||||
|
||||
export default /*#__PURE__*/_defineComponent({
|
||||
export default /*@__PURE__*/_defineComponent({
|
||||
props: {
|
||||
foo: { type: Number, required: false, default: 1 },
|
||||
bar: { type: Object, required: false, default: () => ({}) },
|
||||
|
@ -157,7 +157,7 @@ return () => {}
|
|||
exports[`sfc reactive props destructure > default values w/ type declaration, prod mode 1`] = `
|
||||
"import { defineComponent as _defineComponent } from 'vue'
|
||||
|
||||
export default /*#__PURE__*/_defineComponent({
|
||||
export default /*@__PURE__*/_defineComponent({
|
||||
props: {
|
||||
foo: { default: 1 },
|
||||
bar: { default: () => ({}) },
|
||||
|
@ -320,3 +320,22 @@ return { rest }
|
|||
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`sfc reactive props destructure > with TSInstantiationExpression 1`] = `
|
||||
"import { defineComponent as _defineComponent } from 'vue'
|
||||
type Foo = <T extends string | number>(data: T) => void
|
||||
|
||||
export default /*@__PURE__*/_defineComponent({
|
||||
props: {
|
||||
value: { type: Function }
|
||||
},
|
||||
setup(__props: any) {
|
||||
|
||||
|
||||
const foo = __props.value<123>
|
||||
|
||||
return () => {}
|
||||
}
|
||||
|
||||
})"
|
||||
`;
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
exports[`defineSlots() > basic usage 1`] = `
|
||||
"import { useSlots as _useSlots, defineComponent as _defineComponent } from 'vue'
|
||||
|
||||
export default /*#__PURE__*/_defineComponent({
|
||||
export default /*@__PURE__*/_defineComponent({
|
||||
setup(__props, { expose: __expose }) {
|
||||
__expose();
|
||||
|
||||
|
@ -33,7 +33,7 @@ return { slots }
|
|||
exports[`defineSlots() > w/o return value 1`] = `
|
||||
"import { defineComponent as _defineComponent } from 'vue'
|
||||
|
||||
export default /*#__PURE__*/_defineComponent({
|
||||
export default /*@__PURE__*/_defineComponent({
|
||||
setup(__props, { expose: __expose }) {
|
||||
__expose();
|
||||
|
||||
|
|
|
@ -71,7 +71,7 @@ return () => {}
|
|||
exports[`sfc hoist static > should not hoist a constant initialized to a reference value 1`] = `
|
||||
"import { defineComponent as _defineComponent } from 'vue'
|
||||
|
||||
export default /*#__PURE__*/_defineComponent({
|
||||
export default /*@__PURE__*/_defineComponent({
|
||||
setup(__props) {
|
||||
|
||||
const KEY1 = Boolean
|
||||
|
|
|
@ -5,7 +5,7 @@ exports[`TS annotations 1`] = `
|
|||
import { Foo, Bar, Baz, Qux, Fred } from './x'
|
||||
const a = 1
|
||||
|
||||
export default /*#__PURE__*/_defineComponent({
|
||||
export default /*@__PURE__*/_defineComponent({
|
||||
setup(__props, { expose: __expose }) {
|
||||
__expose();
|
||||
|
||||
|
@ -22,7 +22,7 @@ exports[`attribute expressions 1`] = `
|
|||
import { bar, baz } from './x'
|
||||
const cond = true
|
||||
|
||||
export default /*#__PURE__*/_defineComponent({
|
||||
export default /*@__PURE__*/_defineComponent({
|
||||
setup(__props, { expose: __expose }) {
|
||||
__expose();
|
||||
|
||||
|
@ -38,7 +38,7 @@ exports[`components 1`] = `
|
|||
import { FooBar, FooBaz, FooQux, foo } from './x'
|
||||
const fooBar: FooBar = 1
|
||||
|
||||
export default /*#__PURE__*/_defineComponent({
|
||||
export default /*@__PURE__*/_defineComponent({
|
||||
setup(__props, { expose: __expose }) {
|
||||
__expose();
|
||||
|
||||
|
@ -53,7 +53,7 @@ exports[`directive 1`] = `
|
|||
"import { defineComponent as _defineComponent } from 'vue'
|
||||
import { vMyDir } from './x'
|
||||
|
||||
export default /*#__PURE__*/_defineComponent({
|
||||
export default /*@__PURE__*/_defineComponent({
|
||||
setup(__props, { expose: __expose }) {
|
||||
__expose();
|
||||
|
||||
|
@ -68,7 +68,7 @@ exports[`dynamic arguments 1`] = `
|
|||
"import { defineComponent as _defineComponent } from 'vue'
|
||||
import { FooBar, foo, bar, unused, baz, msg } from './x'
|
||||
|
||||
export default /*#__PURE__*/_defineComponent({
|
||||
export default /*@__PURE__*/_defineComponent({
|
||||
setup(__props, { expose: __expose }) {
|
||||
__expose();
|
||||
|
||||
|
@ -83,7 +83,7 @@ exports[`js template string interpolations 1`] = `
|
|||
"import { defineComponent as _defineComponent } from 'vue'
|
||||
import { VAR, VAR2, VAR3 } from './x'
|
||||
|
||||
export default /*#__PURE__*/_defineComponent({
|
||||
export default /*@__PURE__*/_defineComponent({
|
||||
setup(__props, { expose: __expose }) {
|
||||
__expose();
|
||||
|
||||
|
@ -98,7 +98,7 @@ exports[`last tag 1`] = `
|
|||
"import { defineComponent as _defineComponent } from 'vue'
|
||||
import { FooBaz, Last } from './x'
|
||||
|
||||
export default /*#__PURE__*/_defineComponent({
|
||||
export default /*@__PURE__*/_defineComponent({
|
||||
setup(__props, { expose: __expose }) {
|
||||
__expose();
|
||||
|
||||
|
@ -113,7 +113,7 @@ exports[`namespace / dot component usage 1`] = `
|
|||
"import { defineComponent as _defineComponent } from 'vue'
|
||||
import * as Foo from './foo'
|
||||
|
||||
export default /*#__PURE__*/_defineComponent({
|
||||
export default /*@__PURE__*/_defineComponent({
|
||||
setup(__props, { expose: __expose }) {
|
||||
__expose();
|
||||
|
||||
|
@ -128,7 +128,7 @@ exports[`property access (whitespace) 1`] = `
|
|||
"import { defineComponent as _defineComponent } from 'vue'
|
||||
import { Foo, Bar, Baz } from './foo'
|
||||
|
||||
export default /*#__PURE__*/_defineComponent({
|
||||
export default /*@__PURE__*/_defineComponent({
|
||||
setup(__props, { expose: __expose }) {
|
||||
__expose();
|
||||
|
||||
|
@ -143,7 +143,7 @@ exports[`property access 1`] = `
|
|||
"import { defineComponent as _defineComponent } from 'vue'
|
||||
import { Foo, Bar, Baz } from './foo'
|
||||
|
||||
export default /*#__PURE__*/_defineComponent({
|
||||
export default /*@__PURE__*/_defineComponent({
|
||||
setup(__props, { expose: __expose }) {
|
||||
__expose();
|
||||
|
||||
|
@ -158,7 +158,7 @@ exports[`spread operator 1`] = `
|
|||
"import { defineComponent as _defineComponent } from 'vue'
|
||||
import { Foo, Bar, Baz } from './foo'
|
||||
|
||||
export default /*#__PURE__*/_defineComponent({
|
||||
export default /*@__PURE__*/_defineComponent({
|
||||
setup(__props, { expose: __expose }) {
|
||||
__expose();
|
||||
|
||||
|
@ -173,7 +173,7 @@ exports[`template ref 1`] = `
|
|||
"import { defineComponent as _defineComponent } from 'vue'
|
||||
import { foo, bar, Baz } from './foo'
|
||||
|
||||
export default /*#__PURE__*/_defineComponent({
|
||||
export default /*@__PURE__*/_defineComponent({
|
||||
setup(__props, { expose: __expose }) {
|
||||
__expose();
|
||||
|
||||
|
@ -188,7 +188,7 @@ exports[`vue interpolations 1`] = `
|
|||
"import { defineComponent as _defineComponent } from 'vue'
|
||||
import { x, y, z, x$y } from './x'
|
||||
|
||||
export default /*#__PURE__*/_defineComponent({
|
||||
export default /*@__PURE__*/_defineComponent({
|
||||
setup(__props, { expose: __expose }) {
|
||||
__expose();
|
||||
|
||||
|
|
|
@ -31,7 +31,7 @@ const emit = defineEmits(['a', 'b'])
|
|||
</script>
|
||||
`)
|
||||
assertCode(content)
|
||||
expect(content).toMatch(`export default /*#__PURE__*/_defineComponent({
|
||||
expect(content).toMatch(`export default /*@__PURE__*/_defineComponent({
|
||||
emits: ['a', 'b'],
|
||||
setup(__props, { expose: __expose, emit: __emit }) {`)
|
||||
expect(content).toMatch('const emit = __emit')
|
||||
|
|
|
@ -47,7 +47,7 @@ describe('defineModel()', () => {
|
|||
`,
|
||||
)
|
||||
assertCode(content)
|
||||
expect(content).toMatch(`props: /*#__PURE__*/_mergeModels({ foo: String }`)
|
||||
expect(content).toMatch(`props: /*@__PURE__*/_mergeModels({ foo: String }`)
|
||||
expect(content).toMatch(`"modelValue": { default: 0 }`)
|
||||
expect(content).toMatch(`const count = _useModel(__props, "modelValue")`)
|
||||
expect(content).not.toMatch('defineModel')
|
||||
|
@ -68,7 +68,7 @@ describe('defineModel()', () => {
|
|||
`,
|
||||
)
|
||||
assertCode(content)
|
||||
expect(content).toMatch(`props: /*#__PURE__*/_mergeModels(['foo', 'bar'], {
|
||||
expect(content).toMatch(`props: /*@__PURE__*/_mergeModels(['foo', 'bar'], {
|
||||
"count": {},
|
||||
"countModifiers": {},
|
||||
})`)
|
||||
|
|
|
@ -12,7 +12,7 @@ describe('defineOptions()', () => {
|
|||
expect(content).not.toMatch('defineOptions')
|
||||
// should include context options in default export
|
||||
expect(content).toMatch(
|
||||
`export default /*#__PURE__*/Object.assign({ name: 'FooApp' }, `,
|
||||
`export default /*@__PURE__*/Object.assign({ name: 'FooApp' }, `,
|
||||
)
|
||||
})
|
||||
|
||||
|
|
|
@ -64,7 +64,7 @@ const props = defineProps({ foo: String })
|
|||
</script>
|
||||
`)
|
||||
assertCode(content)
|
||||
expect(content).toMatch(`export default /*#__PURE__*/_defineComponent({
|
||||
expect(content).toMatch(`export default /*@__PURE__*/_defineComponent({
|
||||
props: { foo: String },
|
||||
setup(__props, { expose: __expose }) {`)
|
||||
})
|
||||
|
@ -261,6 +261,51 @@ const props = defineProps({ foo: String })
|
|||
})
|
||||
})
|
||||
|
||||
test('w/ extends intersection type', () => {
|
||||
const { content, bindings } = compile(`
|
||||
<script setup lang="ts">
|
||||
type Foo = {
|
||||
x?: number;
|
||||
};
|
||||
interface Props extends Foo {
|
||||
z: number
|
||||
y: string
|
||||
}
|
||||
defineProps<Props>()
|
||||
</script>
|
||||
`)
|
||||
assertCode(content)
|
||||
expect(content).toMatch(`z: { type: Number, required: true }`)
|
||||
expect(content).toMatch(`y: { type: String, required: true }`)
|
||||
expect(content).toMatch(`x: { type: Number, required: false }`)
|
||||
expect(bindings).toStrictEqual({
|
||||
x: BindingTypes.PROPS,
|
||||
y: BindingTypes.PROPS,
|
||||
z: BindingTypes.PROPS,
|
||||
})
|
||||
})
|
||||
|
||||
test('w/ intersection type', () => {
|
||||
const { content, bindings } = compile(`
|
||||
<script setup lang="ts">
|
||||
type Foo = {
|
||||
x?: number;
|
||||
};
|
||||
type Bar = {
|
||||
y: string;
|
||||
};
|
||||
defineProps<Foo & Bar>()
|
||||
</script>
|
||||
`)
|
||||
assertCode(content)
|
||||
expect(content).toMatch(`y: { type: String, required: true }`)
|
||||
expect(content).toMatch(`x: { type: Number, required: false }`)
|
||||
expect(bindings).toStrictEqual({
|
||||
x: BindingTypes.PROPS,
|
||||
y: BindingTypes.PROPS,
|
||||
})
|
||||
})
|
||||
|
||||
test('w/ exported interface', () => {
|
||||
const { content, bindings } = compile(`
|
||||
<script setup lang="ts">
|
||||
|
|
|
@ -78,7 +78,7 @@ describe('sfc reactive props destructure', () => {
|
|||
// function
|
||||
// functions need to be marked with a skip marker
|
||||
expect(content)
|
||||
.toMatch(`props: /*#__PURE__*/_mergeDefaults(['foo', 'bar', 'baz'], {
|
||||
.toMatch(`props: /*@__PURE__*/_mergeDefaults(['foo', 'bar', 'baz'], {
|
||||
foo: 1,
|
||||
bar: () => ({}),
|
||||
func: () => {}, __skip_func: true
|
||||
|
@ -98,7 +98,7 @@ describe('sfc reactive props destructure', () => {
|
|||
// safely infer whether runtime type is Function (e.g. if the runtime decl
|
||||
// is imported, or spreads another object)
|
||||
expect(content)
|
||||
.toMatch(`props: /*#__PURE__*/_mergeDefaults({ foo: Number, bar: Object, func: Function, ext: null }, {
|
||||
.toMatch(`props: /*@__PURE__*/_mergeDefaults({ foo: Number, bar: Object, func: Function, ext: null }, {
|
||||
foo: 1,
|
||||
bar: () => ({}),
|
||||
func: () => {}, __skip_func: true,
|
||||
|
@ -122,7 +122,7 @@ describe('sfc reactive props destructure', () => {
|
|||
})
|
||||
|
||||
expect(content).toMatch(`
|
||||
props: /*#__PURE__*/_mergeDefaults(['foo', 'foo:bar'], {
|
||||
props: /*@__PURE__*/_mergeDefaults(['foo', 'foo:bar'], {
|
||||
foo: 1,
|
||||
"foo:bar": 'foo-bar'
|
||||
}),`)
|
||||
|
@ -198,6 +198,21 @@ describe('sfc reactive props destructure', () => {
|
|||
}`)
|
||||
})
|
||||
|
||||
test('with TSInstantiationExpression', () => {
|
||||
const { content } = compile(
|
||||
`
|
||||
<script setup lang="ts">
|
||||
type Foo = <T extends string | number>(data: T) => void
|
||||
const { value } = defineProps<{ value: Foo }>()
|
||||
const foo = value<123>
|
||||
</script>
|
||||
`,
|
||||
{ isProd: true },
|
||||
)
|
||||
assertCode(content)
|
||||
expect(content).toMatch(`const foo = __props.value<123>`)
|
||||
})
|
||||
|
||||
test('aliasing', () => {
|
||||
const { content, bindings } = compile(`
|
||||
<script setup>
|
||||
|
@ -378,14 +393,15 @@ describe('sfc reactive props destructure', () => {
|
|||
).toThrow(`destructure cannot use computed key`)
|
||||
})
|
||||
|
||||
test('should error when used with withDefaults', () => {
|
||||
expect(() =>
|
||||
compile(
|
||||
`<script setup lang="ts">
|
||||
const { foo } = withDefaults(defineProps<{ foo: string }>(), { foo: 'foo' })
|
||||
</script>`,
|
||||
),
|
||||
).toThrow(`withDefaults() is unnecessary when using destructure`)
|
||||
test('should warn when used with withDefaults', () => {
|
||||
compile(
|
||||
`<script setup lang="ts">
|
||||
const { foo } = withDefaults(defineProps<{ foo: string }>(), { foo: 'foo' })
|
||||
</script>`,
|
||||
)
|
||||
expect(
|
||||
`withDefaults() is unnecessary when using destructure`,
|
||||
).toHaveBeenWarned()
|
||||
})
|
||||
|
||||
test('should error if destructure reference local vars', () => {
|
||||
|
|
|
@ -234,3 +234,34 @@ test('namespace / dot component usage', () => {
|
|||
expect(content).toMatch('return { get Foo() { return Foo } }')
|
||||
assertCode(content)
|
||||
})
|
||||
|
||||
test('check when has explicit parse options', () => {
|
||||
const { content } = compile(
|
||||
`
|
||||
<script setup lang="ts">
|
||||
import { x } from './x'
|
||||
</script>
|
||||
<template>
|
||||
{{ x }}
|
||||
</template>
|
||||
`,
|
||||
undefined,
|
||||
{ templateParseOptions: {} },
|
||||
)
|
||||
expect(content).toMatch('return { get x() { return x } }')
|
||||
})
|
||||
|
||||
// #11745
|
||||
test('shorthand binding w/ kebab-case', () => {
|
||||
const { content } = compile(
|
||||
`
|
||||
<script setup lang="ts">
|
||||
import { fooBar } from "./foo.ts"
|
||||
</script>
|
||||
<template>
|
||||
<div :foo-bar></div>
|
||||
</template>
|
||||
`,
|
||||
)
|
||||
expect(content).toMatch('return { get fooBar() { return fooBar }')
|
||||
})
|
||||
|
|
|
@ -596,6 +596,65 @@ describe('resolveType', () => {
|
|||
})
|
||||
})
|
||||
|
||||
test('keyof: nested object with number', () => {
|
||||
const { props } = resolve(
|
||||
`
|
||||
interface Type {
|
||||
deep: {
|
||||
1: any
|
||||
}
|
||||
}
|
||||
|
||||
defineProps<{
|
||||
route: keyof Type['deep']
|
||||
}>()`,
|
||||
)
|
||||
|
||||
expect(props).toStrictEqual({
|
||||
route: ['Number'],
|
||||
})
|
||||
})
|
||||
|
||||
test('keyof: nested object with string', () => {
|
||||
const { props } = resolve(
|
||||
`
|
||||
interface Type {
|
||||
deep: {
|
||||
foo: any
|
||||
}
|
||||
}
|
||||
|
||||
defineProps<{
|
||||
route: keyof Type['deep']
|
||||
}>()`,
|
||||
)
|
||||
|
||||
expect(props).toStrictEqual({
|
||||
route: ['String'],
|
||||
})
|
||||
})
|
||||
|
||||
test('keyof: nested object with intermediate', () => {
|
||||
const { props } = resolve(
|
||||
`
|
||||
interface Type {
|
||||
deep: {
|
||||
foo: any
|
||||
}
|
||||
}
|
||||
|
||||
type Foo = Type['deep']
|
||||
|
||||
defineProps<{
|
||||
route: keyof Foo
|
||||
}>()`,
|
||||
)
|
||||
|
||||
expect(props).toStrictEqual({
|
||||
route: ['String'],
|
||||
})
|
||||
})
|
||||
|
||||
test('ExtractPropTypes (element-plus)', () => {
|
||||
const { props, raw } = resolve(
|
||||
`
|
||||
|
@ -1126,6 +1185,49 @@ describe('resolveType', () => {
|
|||
expect(deps && [...deps]).toStrictEqual(['/user.ts'])
|
||||
})
|
||||
|
||||
// #11382
|
||||
test('ts module resolve circular project reference', () => {
|
||||
const files = {
|
||||
'/tsconfig.json': JSON.stringify({
|
||||
exclude: ['**/*.ts', '**/*.vue'],
|
||||
references: [
|
||||
{
|
||||
path: './tsconfig.web.json',
|
||||
},
|
||||
],
|
||||
}),
|
||||
'/tsconfig.web.json': JSON.stringify({
|
||||
include: ['**/*.ts', '**/*.vue'],
|
||||
compilerOptions: {
|
||||
composite: true,
|
||||
paths: {
|
||||
user: ['./user.ts'],
|
||||
},
|
||||
},
|
||||
references: [
|
||||
{
|
||||
// circular reference
|
||||
path: './tsconfig.json',
|
||||
},
|
||||
],
|
||||
}),
|
||||
'/user.ts': 'export type User = { bar: string }',
|
||||
}
|
||||
|
||||
const { props, deps } = resolve(
|
||||
`
|
||||
import { User } from 'user'
|
||||
defineProps<User>()
|
||||
`,
|
||||
files,
|
||||
)
|
||||
|
||||
expect(props).toStrictEqual({
|
||||
bar: ['String'],
|
||||
})
|
||||
expect(deps && [...deps]).toStrictEqual(['/user.ts'])
|
||||
})
|
||||
|
||||
test('ts module resolve w/ path aliased vue file', () => {
|
||||
const files = {
|
||||
'/tsconfig.json': JSON.stringify({
|
||||
|
|
|
@ -41,6 +41,44 @@ describe('SFC scoped CSS', () => {
|
|||
)
|
||||
})
|
||||
|
||||
test('nesting selector', () => {
|
||||
expect(compileScoped(`h1 { color: red; .foo { color: red; } }`)).toMatch(
|
||||
`h1 {\n&[data-v-test] { color: red;\n}\n.foo[data-v-test] { color: red;`,
|
||||
)
|
||||
})
|
||||
|
||||
test('nesting selector with atrule and comment', () => {
|
||||
expect(
|
||||
compileScoped(
|
||||
`h1 {
|
||||
color: red;
|
||||
/*background-color: pink;*/
|
||||
@media only screen and (max-width: 800px) {
|
||||
background-color: green;
|
||||
.bar { color: white }
|
||||
}
|
||||
.foo { color: red; }
|
||||
}`,
|
||||
),
|
||||
).toMatch(
|
||||
`h1 {
|
||||
&[data-v-test] {
|
||||
color: red
|
||||
/*background-color: pink;*/
|
||||
}
|
||||
@media only screen and (max-width: 800px) {
|
||||
&[data-v-test] {
|
||||
background-color: green
|
||||
}
|
||||
.bar[data-v-test] { color: white
|
||||
}
|
||||
}
|
||||
.foo[data-v-test] { color: red;
|
||||
}
|
||||
}`,
|
||||
)
|
||||
})
|
||||
|
||||
test('multiple selectors', () => {
|
||||
expect(compileScoped(`h1 .foo, .bar, .baz { color: red; }`)).toMatch(
|
||||
`h1 .foo[data-v-test], .bar[data-v-test], .baz[data-v-test] { color: red;`,
|
||||
|
@ -95,6 +133,13 @@ describe('SFC scoped CSS', () => {
|
|||
":where(.foo[data-v-test] .bar) { color: red;
|
||||
}"
|
||||
`)
|
||||
expect(compileScoped(`:deep(.foo) { color: red; .bar { color: red; } }`))
|
||||
.toMatchInlineSnapshot(`
|
||||
"[data-v-test] .foo { color: red;
|
||||
.bar { color: red;
|
||||
}
|
||||
}"
|
||||
`)
|
||||
})
|
||||
|
||||
test('::v-slotted', () => {
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import {
|
||||
type SFCParseOptions,
|
||||
type SFCScriptBlock,
|
||||
type SFCScriptCompileOptions,
|
||||
compileScript,
|
||||
parse,
|
||||
|
@ -12,7 +13,7 @@ export function compileSFCScript(
|
|||
src: string,
|
||||
options?: Partial<SFCScriptCompileOptions>,
|
||||
parseOptions?: SFCParseOptions,
|
||||
) {
|
||||
): SFCScriptBlock {
|
||||
const { descriptor, errors } = parse(src, parseOptions)
|
||||
if (errors.length) {
|
||||
console.warn(errors[0])
|
||||
|
@ -23,7 +24,7 @@ export function compileSFCScript(
|
|||
})
|
||||
}
|
||||
|
||||
export function assertCode(code: string) {
|
||||
export function assertCode(code: string): void {
|
||||
// parse the generated code to make sure it is valid
|
||||
try {
|
||||
babelParse(code, {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@vue/compiler-sfc",
|
||||
"version": "3.5.0-beta.1",
|
||||
"version": "3.5.13",
|
||||
"description": "@vue/compiler-sfc",
|
||||
"main": "dist/compiler-sfc.cjs.js",
|
||||
"module": "dist/compiler-sfc.esm-browser.js",
|
||||
|
@ -49,7 +49,7 @@
|
|||
"@vue/shared": "workspace:*",
|
||||
"estree-walker": "catalog:",
|
||||
"magic-string": "catalog:",
|
||||
"postcss": "^8.4.41",
|
||||
"postcss": "^8.4.49",
|
||||
"source-map-js": "catalog:"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
@ -59,9 +59,9 @@
|
|||
"lru-cache": "10.1.0",
|
||||
"merge-source-map": "^1.1.0",
|
||||
"minimatch": "~9.0.5",
|
||||
"postcss-modules": "^6.0.0",
|
||||
"postcss-selector-parser": "^6.1.1",
|
||||
"postcss-modules": "^6.0.1",
|
||||
"postcss-selector-parser": "^7.0.0",
|
||||
"pug": "^3.0.3",
|
||||
"sass": "^1.77.8"
|
||||
"sass": "^1.83.0"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ import { LRUCache } from 'lru-cache'
|
|||
export function createCache<T extends {}>(
|
||||
max = 500,
|
||||
): Map<string, T> | LRUCache<string, T> {
|
||||
/* v8 ignore next 3 */
|
||||
if (__GLOBAL__ || __ESM_BROWSER__) {
|
||||
return new Map<string, T>()
|
||||
}
|
||||
|
|
|
@ -979,7 +979,7 @@ export function compileScript(
|
|||
(definedOptions ? `\n ...${definedOptions},` : '')
|
||||
ctx.s.prependLeft(
|
||||
startOffset,
|
||||
`\n${genDefaultAs} /*#__PURE__*/${ctx.helper(
|
||||
`\n${genDefaultAs} /*@__PURE__*/${ctx.helper(
|
||||
`defineComponent`,
|
||||
)}({${def}${runtimeOptions}\n ${
|
||||
hasAwait ? `async ` : ``
|
||||
|
@ -992,7 +992,7 @@ export function compileScript(
|
|||
// export default Object.assign(__default__, { ... })
|
||||
ctx.s.prependLeft(
|
||||
startOffset,
|
||||
`\n${genDefaultAs} /*#__PURE__*/Object.assign(${
|
||||
`\n${genDefaultAs} /*@__PURE__*/Object.assign(${
|
||||
defaultExport ? `${normalScriptDefaultVar}, ` : ''
|
||||
}${definedOptions ? `${definedOptions}, ` : ''}{${runtimeOptions}\n ` +
|
||||
`${hasAwait ? `async ` : ``}setup(${args}) {\n${exposeCall}`,
|
||||
|
|
|
@ -18,6 +18,7 @@ import { createCache } from './cache'
|
|||
import type { ImportBinding } from './compileScript'
|
||||
import { isImportUsed } from './script/importUsageCheck'
|
||||
import type { LRUCache } from 'lru-cache'
|
||||
import { genCacheKey } from '@vue/shared'
|
||||
|
||||
export const DEFAULT_FILENAME = 'anonymous.vue'
|
||||
|
||||
|
@ -29,11 +30,6 @@ export interface SFCParseOptions {
|
|||
ignoreEmpty?: boolean
|
||||
compiler?: TemplateCompiler
|
||||
templateParseOptions?: ParserOptions
|
||||
/**
|
||||
* TODO remove in 3.5
|
||||
* @deprecated use `templateParseOptions: { prefixIdentifiers: false }` instead
|
||||
*/
|
||||
parseExpressions?: boolean
|
||||
}
|
||||
|
||||
export interface SFCBlock {
|
||||
|
@ -108,24 +104,14 @@ export const parseCache:
|
|||
| Map<string, SFCParseResult>
|
||||
| LRUCache<string, SFCParseResult> = createCache<SFCParseResult>()
|
||||
|
||||
function genCacheKey(source: string, options: SFCParseOptions): string {
|
||||
return (
|
||||
source +
|
||||
JSON.stringify(
|
||||
{
|
||||
...options,
|
||||
compiler: { parse: options.compiler?.parse },
|
||||
},
|
||||
(_, val) => (typeof val === 'function' ? val.toString() : val),
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
export function parse(
|
||||
source: string,
|
||||
options: SFCParseOptions = {},
|
||||
): SFCParseResult {
|
||||
const sourceKey = genCacheKey(source, options)
|
||||
const sourceKey = genCacheKey(source, {
|
||||
...options,
|
||||
compiler: { parse: options.compiler?.parse },
|
||||
})
|
||||
const cache = parseCache.get(sourceKey)
|
||||
if (cache) {
|
||||
return cache
|
||||
|
@ -139,7 +125,6 @@ export function parse(
|
|||
ignoreEmpty = true,
|
||||
compiler = CompilerDOM,
|
||||
templateParseOptions = {},
|
||||
parseExpressions = true,
|
||||
} = options
|
||||
|
||||
const descriptor: SFCDescriptor = {
|
||||
|
@ -158,7 +143,7 @@ export function parse(
|
|||
const errors: (CompilerError | SyntaxError)[] = []
|
||||
const ast = compiler.parse(source, {
|
||||
parseMode: 'sfc',
|
||||
prefixIdentifiers: parseExpressions,
|
||||
prefixIdentifiers: true,
|
||||
...templateParseOptions,
|
||||
onError: e => {
|
||||
errors.push(e)
|
||||
|
@ -241,7 +226,7 @@ export function parse(
|
|||
if (!descriptor.template && !descriptor.script && !descriptor.scriptSetup) {
|
||||
errors.push(
|
||||
new SyntaxError(
|
||||
`At least one <template> or <script> is required in a single file component.`,
|
||||
`At least one <template> or <script> is required in a single file component. ${descriptor.filename}`,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ import type { ModelDecl } from './defineModel'
|
|||
import type { BindingMetadata } from '../../../compiler-core/src'
|
||||
import MagicString from 'magic-string'
|
||||
import type { TypeScope } from './resolveType'
|
||||
import { warn } from '../warn'
|
||||
|
||||
export class ScriptCompileContext {
|
||||
isJS: boolean
|
||||
|
@ -145,20 +146,31 @@ export class ScriptCompileContext {
|
|||
return block.content.slice(node.start!, node.end!)
|
||||
}
|
||||
|
||||
warn(msg: string, node: Node, scope?: TypeScope): void {
|
||||
warn(generateError(msg, node, this, scope))
|
||||
}
|
||||
|
||||
error(msg: string, node: Node, scope?: TypeScope): never {
|
||||
const offset = scope ? scope.offset : this.startOffset!
|
||||
throw new Error(
|
||||
`[@vue/compiler-sfc] ${msg}\n\n${
|
||||
(scope || this.descriptor).filename
|
||||
}\n${generateCodeFrame(
|
||||
(scope || this.descriptor).source,
|
||||
node.start! + offset,
|
||||
node.end! + offset,
|
||||
)}`,
|
||||
`[@vue/compiler-sfc] ${generateError(msg, node, this, scope)}`,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
function generateError(
|
||||
msg: string,
|
||||
node: Node,
|
||||
ctx: ScriptCompileContext,
|
||||
scope?: TypeScope,
|
||||
) {
|
||||
const offset = scope ? scope.offset : ctx.startOffset!
|
||||
return `${msg}\n\n${(scope || ctx.descriptor).filename}\n${generateCodeFrame(
|
||||
(scope || ctx.descriptor).source,
|
||||
node.start! + offset,
|
||||
node.end! + offset,
|
||||
)}`
|
||||
}
|
||||
|
||||
export function resolveParserPlugins(
|
||||
lang: string,
|
||||
userPlugins?: ParserPlugin[],
|
||||
|
|
|
@ -62,7 +62,7 @@ export function genRuntimeEmits(ctx: ScriptCompileContext): string | undefined {
|
|||
.map(n => JSON.stringify(`update:${n}`))
|
||||
.join(', ')}]`
|
||||
emitsDecl = emitsDecl
|
||||
? `/*#__PURE__*/${ctx.helper(
|
||||
? `/*@__PURE__*/${ctx.helper(
|
||||
'mergeModels',
|
||||
)}(${emitsDecl}, ${modelEmitsDecl})`
|
||||
: modelEmitsDecl
|
||||
|
|
|
@ -48,6 +48,7 @@ export function processDefineProps(
|
|||
ctx: ScriptCompileContext,
|
||||
node: Node,
|
||||
declId?: LVal,
|
||||
isWithDefaults = false,
|
||||
): boolean {
|
||||
if (!isCallOf(node, DEFINE_PROPS)) {
|
||||
return processWithDefaults(ctx, node, declId)
|
||||
|
@ -81,7 +82,7 @@ export function processDefineProps(
|
|||
}
|
||||
|
||||
// handle props destructure
|
||||
if (declId && declId.type === 'ObjectPattern') {
|
||||
if (!isWithDefaults && declId && declId.type === 'ObjectPattern') {
|
||||
processPropsDestructure(ctx, declId)
|
||||
}
|
||||
|
||||
|
@ -99,7 +100,14 @@ function processWithDefaults(
|
|||
if (!isCallOf(node, WITH_DEFAULTS)) {
|
||||
return false
|
||||
}
|
||||
if (!processDefineProps(ctx, node.arguments[0], declId)) {
|
||||
if (
|
||||
!processDefineProps(
|
||||
ctx,
|
||||
node.arguments[0],
|
||||
declId,
|
||||
true /* isWithDefaults */,
|
||||
)
|
||||
) {
|
||||
ctx.error(
|
||||
`${WITH_DEFAULTS}' first argument must be a ${DEFINE_PROPS} call.`,
|
||||
node.arguments[0] || node,
|
||||
|
@ -113,10 +121,11 @@ function processWithDefaults(
|
|||
node,
|
||||
)
|
||||
}
|
||||
if (ctx.propsDestructureDecl) {
|
||||
ctx.error(
|
||||
if (declId && declId.type === 'ObjectPattern') {
|
||||
ctx.warn(
|
||||
`${WITH_DEFAULTS}() is unnecessary when using destructure with ${DEFINE_PROPS}().\n` +
|
||||
`Prefer using destructure default values, e.g. const { foo = 1 } = defineProps(...).`,
|
||||
`Reactive destructure will be disabled when using withDefaults().\n` +
|
||||
`Prefer using destructure default values, e.g. const { foo = 1 } = defineProps(...). `,
|
||||
node.callee,
|
||||
)
|
||||
}
|
||||
|
@ -147,7 +156,7 @@ export function genRuntimeProps(ctx: ScriptCompileContext): string | undefined {
|
|||
)
|
||||
}
|
||||
if (defaults.length) {
|
||||
propsDecls = `/*#__PURE__*/${ctx.helper(
|
||||
propsDecls = `/*@__PURE__*/${ctx.helper(
|
||||
`mergeDefaults`,
|
||||
)}(${propsDecls}, {\n ${defaults.join(',\n ')}\n})`
|
||||
}
|
||||
|
@ -159,7 +168,7 @@ export function genRuntimeProps(ctx: ScriptCompileContext): string | undefined {
|
|||
const modelsDecls = genModelProps(ctx)
|
||||
|
||||
if (propsDecls && modelsDecls) {
|
||||
return `/*#__PURE__*/${ctx.helper(
|
||||
return `/*@__PURE__*/${ctx.helper(
|
||||
'mergeModels',
|
||||
)}(${propsDecls}, ${modelsDecls})`
|
||||
} else {
|
||||
|
@ -191,7 +200,7 @@ export function extractRuntimeProps(
|
|||
${propStrings.join(',\n ')}\n }`
|
||||
|
||||
if (ctx.propsRuntimeDefaults && !hasStaticDefaults) {
|
||||
propsDecls = `/*#__PURE__*/${ctx.helper(
|
||||
propsDecls = `/*@__PURE__*/${ctx.helper(
|
||||
'mergeDefaults',
|
||||
)}(${propsDecls}, ${ctx.getString(ctx.propsRuntimeDefaults)})`
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ import type {
|
|||
import { walk } from 'estree-walker'
|
||||
import {
|
||||
BindingTypes,
|
||||
TS_NODE_TYPES,
|
||||
extractIdentifiers,
|
||||
isFunctionType,
|
||||
isInDestructureAssignment,
|
||||
|
@ -102,7 +103,7 @@ export function transformDestructuredProps(
|
|||
return
|
||||
}
|
||||
|
||||
const rootScope: Scope = {}
|
||||
const rootScope: Scope = Object.create(null)
|
||||
const scopeStack: Scope[] = [rootScope]
|
||||
let currentScope: Scope = rootScope
|
||||
const excludedIds = new WeakSet<Identifier>()
|
||||
|
@ -240,9 +241,7 @@ export function transformDestructuredProps(
|
|||
if (
|
||||
parent &&
|
||||
parent.type.startsWith('TS') &&
|
||||
parent.type !== 'TSAsExpression' &&
|
||||
parent.type !== 'TSNonNullExpression' &&
|
||||
parent.type !== 'TSTypeAssertion'
|
||||
!TS_NODE_TYPES.includes(parent.type)
|
||||
) {
|
||||
return this.skip()
|
||||
}
|
||||
|
|
|
@ -62,7 +62,7 @@ function resolveTemplateUsedIdentifiers(sfc: SFCDescriptor): Set<string> {
|
|||
extractIdentifiers(ids, prop.exp)
|
||||
} else if (prop.name === 'bind' && !prop.exp) {
|
||||
// v-bind shorthand name as identifier
|
||||
ids.add((prop.arg as SimpleExpressionNode).content)
|
||||
ids.add(camelize((prop.arg as SimpleExpressionNode).content))
|
||||
}
|
||||
}
|
||||
if (
|
||||
|
|
|
@ -1070,6 +1070,7 @@ function loadTSConfig(
|
|||
configPath: string,
|
||||
ts: typeof TS,
|
||||
fs: FS,
|
||||
visited = new Set<string>(),
|
||||
): TS.ParsedCommandLine[] {
|
||||
// The only case where `fs` is NOT `ts.sys` is during tests.
|
||||
// parse config host requires an extra `readDirectory` method
|
||||
|
@ -1089,14 +1090,15 @@ function loadTSConfig(
|
|||
configPath,
|
||||
)
|
||||
const res = [config]
|
||||
visited.add(configPath)
|
||||
if (config.projectReferences) {
|
||||
for (const ref of config.projectReferences) {
|
||||
const refPath = ts.resolveProjectReferencePath(ref)
|
||||
if (!fs.fileExists(refPath)) {
|
||||
if (visited.has(refPath) || !fs.fileExists(refPath)) {
|
||||
continue
|
||||
}
|
||||
tsConfigRefMap.set(refPath, configPath)
|
||||
res.unshift(...loadTSConfig(refPath, ts, fs))
|
||||
res.unshift(...loadTSConfig(refPath, ts, fs, visited))
|
||||
}
|
||||
}
|
||||
return res
|
||||
|
@ -1703,7 +1705,7 @@ export function inferRuntimeType(
|
|||
|
||||
case 'TSIndexedAccessType': {
|
||||
const types = resolveIndexType(ctx, node, scope)
|
||||
return flattenTypes(ctx, types, scope)
|
||||
return flattenTypes(ctx, types, scope, isKeyOf)
|
||||
}
|
||||
|
||||
case 'ClassDeclaration':
|
||||
|
|
|
@ -121,15 +121,3 @@ export const propNameEscapeSymbolsRE: RegExp =
|
|||
export function getEscapedPropName(key: string): string {
|
||||
return propNameEscapeSymbolsRE.test(key) ? JSON.stringify(key) : key
|
||||
}
|
||||
|
||||
export const cssVarNameEscapeSymbolsRE: RegExp =
|
||||
/[ !"#$%&'()*+,./:;<=>?@[\\\]^`{|}~]/g
|
||||
|
||||
export function getEscapedCssVarName(
|
||||
key: string,
|
||||
doubleEscape: boolean,
|
||||
): string {
|
||||
return key.replace(cssVarNameEscapeSymbolsRE, s =>
|
||||
doubleEscape ? `\\\\${s}` : `\\${s}`,
|
||||
)
|
||||
}
|
||||
|
|
|
@ -8,9 +8,9 @@ import {
|
|||
processExpression,
|
||||
} from '@vue/compiler-dom'
|
||||
import type { SFCDescriptor } from '../parse'
|
||||
import { getEscapedCssVarName } from '../script/utils'
|
||||
import type { PluginCreator } from 'postcss'
|
||||
import hash from 'hash-sum'
|
||||
import { getEscapedCssVarName } from '@vue/shared'
|
||||
|
||||
export const CSS_VARS_HELPER = `useCssVars`
|
||||
|
||||
|
|
|
@ -1,4 +1,10 @@
|
|||
import type { AtRule, PluginCreator, Rule } from 'postcss'
|
||||
import {
|
||||
type AtRule,
|
||||
type Container,
|
||||
type Document,
|
||||
type PluginCreator,
|
||||
Rule,
|
||||
} from 'postcss'
|
||||
import selectorParser from 'postcss-selector-parser'
|
||||
import { warn } from '../warn'
|
||||
|
||||
|
@ -71,21 +77,32 @@ function processRule(id: string, rule: Rule) {
|
|||
return
|
||||
}
|
||||
processedRules.add(rule)
|
||||
let deep = false
|
||||
let parent: Document | Container | undefined = rule.parent
|
||||
while (parent && parent.type !== 'root') {
|
||||
if ((parent as any).__deep) {
|
||||
deep = true
|
||||
break
|
||||
}
|
||||
parent = parent.parent
|
||||
}
|
||||
rule.selector = selectorParser(selectorRoot => {
|
||||
selectorRoot.each(selector => {
|
||||
rewriteSelector(id, selector, selectorRoot)
|
||||
rewriteSelector(id, rule, selector, selectorRoot, deep)
|
||||
})
|
||||
}).processSync(rule.selector)
|
||||
}
|
||||
|
||||
function rewriteSelector(
|
||||
id: string,
|
||||
rule: Rule,
|
||||
selector: selectorParser.Selector,
|
||||
selectorRoot: selectorParser.Root,
|
||||
deep: boolean,
|
||||
slotted = false,
|
||||
) {
|
||||
let node: selectorParser.Node | null = null
|
||||
let shouldInject = true
|
||||
let shouldInject = !deep
|
||||
// find the last child node to insert attribute selector
|
||||
selector.each(n => {
|
||||
// DEPRECATED ">>>" and "/deep/" combinator
|
||||
|
@ -107,6 +124,7 @@ function rewriteSelector(
|
|||
// deep: inject [id] attribute at the node before the ::v-deep
|
||||
// combinator.
|
||||
if (value === ':deep' || value === '::v-deep') {
|
||||
;(rule as any).__deep = true
|
||||
if (n.nodes.length) {
|
||||
// .foo ::v-deep(.bar) -> .foo[xxxxxxx] .bar
|
||||
// replace the current node with ::v-deep's inner selector
|
||||
|
@ -147,7 +165,14 @@ function rewriteSelector(
|
|||
// instead.
|
||||
// ::v-slotted(.foo) -> .foo[xxxxxxx-s]
|
||||
if (value === ':slotted' || value === '::v-slotted') {
|
||||
rewriteSelector(id, n.nodes[0], selectorRoot, true /* slotted */)
|
||||
rewriteSelector(
|
||||
id,
|
||||
rule,
|
||||
n.nodes[0],
|
||||
selectorRoot,
|
||||
deep,
|
||||
true /* slotted */,
|
||||
)
|
||||
let last: selectorParser.Selector['nodes'][0] = n
|
||||
n.nodes[0].each(ss => {
|
||||
selector.insertAfter(last, ss)
|
||||
|
@ -164,8 +189,7 @@ function rewriteSelector(
|
|||
// global: replace with inner selector and do not inject [id].
|
||||
// ::v-global(.foo) -> .foo
|
||||
if (value === ':global' || value === '::v-global') {
|
||||
selectorRoot.insertAfter(selector, n.nodes[0])
|
||||
selectorRoot.removeChild(selector)
|
||||
selector.replaceWith(n.nodes[0])
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
@ -206,11 +230,23 @@ function rewriteSelector(
|
|||
}
|
||||
})
|
||||
|
||||
if (rule.nodes.some(node => node.type === 'rule')) {
|
||||
const deep = (rule as any).__deep
|
||||
if (!deep) {
|
||||
extractAndWrapNodes(rule)
|
||||
const atruleNodes = rule.nodes.filter(node => node.type === 'atrule')
|
||||
for (const atnode of atruleNodes) {
|
||||
extractAndWrapNodes(atnode)
|
||||
}
|
||||
}
|
||||
shouldInject = deep
|
||||
}
|
||||
|
||||
if (node) {
|
||||
const { type, value } = node as selectorParser.Node
|
||||
if (type === 'pseudo' && (value === ':is' || value === ':where')) {
|
||||
;(node as selectorParser.Pseudo).nodes.forEach(value =>
|
||||
rewriteSelector(id, value, selectorRoot, slotted),
|
||||
rewriteSelector(id, rule, value, selectorRoot, deep, slotted),
|
||||
)
|
||||
shouldInject = false
|
||||
}
|
||||
|
@ -245,5 +281,22 @@ function isSpaceCombinator(node: selectorParser.Node) {
|
|||
return node.type === 'combinator' && /^\s+$/.test(node.value)
|
||||
}
|
||||
|
||||
function extractAndWrapNodes(parentNode: Rule | AtRule) {
|
||||
if (!parentNode.nodes) return
|
||||
const nodes = parentNode.nodes.filter(
|
||||
node => node.type === 'decl' || node.type === 'comment',
|
||||
)
|
||||
if (nodes.length) {
|
||||
for (const node of nodes) {
|
||||
parentNode.removeChild(node)
|
||||
}
|
||||
const wrappedRule = new Rule({
|
||||
nodes: nodes,
|
||||
selector: '&',
|
||||
})
|
||||
parentNode.prepend(wrappedRule)
|
||||
}
|
||||
}
|
||||
|
||||
scopedPlugin.postcss = true
|
||||
export default scopedPlugin
|
||||
|
|
|
@ -23,28 +23,48 @@ export interface StylePreprocessorResults {
|
|||
|
||||
// .scss/.sass processor
|
||||
const scss: StylePreprocessor = (source, map, options, load = require) => {
|
||||
const nodeSass = load('sass')
|
||||
const finalOptions = {
|
||||
...options,
|
||||
data: getSource(source, options.filename, options.additionalData),
|
||||
file: options.filename,
|
||||
outFile: options.filename,
|
||||
sourceMap: !!map,
|
||||
}
|
||||
const nodeSass: typeof import('sass') = load('sass')
|
||||
const { compileString, renderSync } = nodeSass
|
||||
|
||||
const data = getSource(source, options.filename, options.additionalData)
|
||||
let css: string
|
||||
let dependencies: string[]
|
||||
let sourceMap: any
|
||||
|
||||
try {
|
||||
const result = nodeSass.renderSync(finalOptions)
|
||||
const dependencies = result.stats.includedFiles
|
||||
if (map) {
|
||||
return {
|
||||
code: result.css.toString(),
|
||||
map: merge(map, JSON.parse(result.map.toString())),
|
||||
errors: [],
|
||||
dependencies,
|
||||
}
|
||||
if (compileString) {
|
||||
const { pathToFileURL, fileURLToPath }: typeof import('url') = load('url')
|
||||
|
||||
const result = compileString(data, {
|
||||
...options,
|
||||
url: pathToFileURL(options.filename),
|
||||
sourceMap: !!map,
|
||||
})
|
||||
css = result.css
|
||||
dependencies = result.loadedUrls.map(url => fileURLToPath(url))
|
||||
sourceMap = map ? result.sourceMap! : undefined
|
||||
} else {
|
||||
const result = renderSync({
|
||||
...options,
|
||||
data,
|
||||
file: options.filename,
|
||||
outFile: options.filename,
|
||||
sourceMap: !!map,
|
||||
})
|
||||
css = result.css.toString()
|
||||
dependencies = result.stats.includedFiles
|
||||
sourceMap = map ? JSON.parse(result.map!.toString()) : undefined
|
||||
}
|
||||
|
||||
return { code: result.css.toString(), errors: [], dependencies }
|
||||
if (map) {
|
||||
return {
|
||||
code: css,
|
||||
errors: [],
|
||||
dependencies,
|
||||
map: merge(map, sourceMap!),
|
||||
}
|
||||
}
|
||||
return { code: css, errors: [], dependencies }
|
||||
} catch (e: any) {
|
||||
return { code: '', errors: [e], dependencies: [] }
|
||||
}
|
||||
|
|
|
@ -337,6 +337,39 @@ describe('ssr: element', () => {
|
|||
`)
|
||||
})
|
||||
|
||||
test('custom dir with v-text', () => {
|
||||
expect(getCompiledString(`<div v-xxx v-text="foo" />`))
|
||||
.toMatchInlineSnapshot(`
|
||||
"\`<div\${
|
||||
_ssrRenderAttrs(_ssrGetDirectiveProps(_ctx, _directive_xxx))
|
||||
}>\${
|
||||
_ssrInterpolate(_ctx.foo)
|
||||
}</div>\`"
|
||||
`)
|
||||
})
|
||||
|
||||
test('custom dir with v-text and normal attrs', () => {
|
||||
expect(getCompiledString(`<div class="test" v-xxx v-text="foo" />`))
|
||||
.toMatchInlineSnapshot(`
|
||||
"\`<div\${
|
||||
_ssrRenderAttrs(_mergeProps({ class: "test" }, _ssrGetDirectiveProps(_ctx, _directive_xxx)))
|
||||
}>\${
|
||||
_ssrInterpolate(_ctx.foo)
|
||||
}</div>\`"
|
||||
`)
|
||||
})
|
||||
|
||||
test('mulptiple custom dirs with v-text', () => {
|
||||
expect(getCompiledString(`<div v-xxx v-yyy v-text="foo" />`))
|
||||
.toMatchInlineSnapshot(`
|
||||
"\`<div\${
|
||||
_ssrRenderAttrs(_mergeProps(_ssrGetDirectiveProps(_ctx, _directive_xxx), _ssrGetDirectiveProps(_ctx, _directive_yyy)))
|
||||
}>\${
|
||||
_ssrInterpolate(_ctx.foo)
|
||||
}</div>\`"
|
||||
`)
|
||||
})
|
||||
|
||||
test('custom dir with object v-bind', () => {
|
||||
expect(getCompiledString(`<div v-bind="x" v-xxx />`))
|
||||
.toMatchInlineSnapshot(`
|
||||
|
|
|
@ -39,7 +39,7 @@ describe('transition-group', () => {
|
|||
})
|
||||
|
||||
// #11514
|
||||
test('with static tag + comment', () => {
|
||||
test('with static tag + v-if comment', () => {
|
||||
expect(
|
||||
compile(
|
||||
`<transition-group tag="ul"><div v-for="i in list"/><div v-if="false"></div></transition-group>`,
|
||||
|
@ -60,6 +60,25 @@ describe('transition-group', () => {
|
|||
`)
|
||||
})
|
||||
|
||||
// #11958
|
||||
test('with static tag + comment', () => {
|
||||
expect(
|
||||
compile(
|
||||
`<transition-group tag="ul"><div v-for="i in list"/><!--test--></transition-group>`,
|
||||
).code,
|
||||
).toMatchInlineSnapshot(`
|
||||
"const { ssrRenderAttrs: _ssrRenderAttrs, ssrRenderList: _ssrRenderList } = require("vue/server-renderer")
|
||||
|
||||
return function ssrRender(_ctx, _push, _parent, _attrs) {
|
||||
_push(\`<ul\${_ssrRenderAttrs(_attrs)}>\`)
|
||||
_ssrRenderList(_ctx.list, (i) => {
|
||||
_push(\`<div></div>\`)
|
||||
})
|
||||
_push(\`</ul>\`)
|
||||
}"
|
||||
`)
|
||||
})
|
||||
|
||||
test('with dynamic tag', () => {
|
||||
expect(
|
||||
compile(
|
||||
|
|
|
@ -52,6 +52,52 @@ describe('ssr: v-model', () => {
|
|||
}"
|
||||
`)
|
||||
|
||||
expect(
|
||||
compileWithWrapper(
|
||||
`<select v-model="model"><option v-for="i in items" :value="i"></option></select>`,
|
||||
).code,
|
||||
).toMatchInlineSnapshot(`
|
||||
"const { ssrRenderAttr: _ssrRenderAttr, ssrIncludeBooleanAttr: _ssrIncludeBooleanAttr, ssrLooseContain: _ssrLooseContain, ssrLooseEqual: _ssrLooseEqual, ssrRenderAttrs: _ssrRenderAttrs, ssrRenderList: _ssrRenderList } = require("vue/server-renderer")
|
||||
|
||||
return function ssrRender(_ctx, _push, _parent, _attrs) {
|
||||
_push(\`<div\${_ssrRenderAttrs(_attrs)}><select><!--[-->\`)
|
||||
_ssrRenderList(_ctx.items, (i) => {
|
||||
_push(\`<option\${
|
||||
_ssrRenderAttr("value", i)
|
||||
}\${
|
||||
(_ssrIncludeBooleanAttr((Array.isArray(_ctx.model))
|
||||
? _ssrLooseContain(_ctx.model, i)
|
||||
: _ssrLooseEqual(_ctx.model, i))) ? " selected" : ""
|
||||
}></option>\`)
|
||||
})
|
||||
_push(\`<!--]--></select></div>\`)
|
||||
}"
|
||||
`)
|
||||
|
||||
expect(
|
||||
compileWithWrapper(
|
||||
`<select v-model="model"><option v-if="true" :value="i"></option></select>`,
|
||||
).code,
|
||||
).toMatchInlineSnapshot(`
|
||||
"const { ssrRenderAttr: _ssrRenderAttr, ssrIncludeBooleanAttr: _ssrIncludeBooleanAttr, ssrLooseContain: _ssrLooseContain, ssrLooseEqual: _ssrLooseEqual, ssrRenderAttrs: _ssrRenderAttrs } = require("vue/server-renderer")
|
||||
|
||||
return function ssrRender(_ctx, _push, _parent, _attrs) {
|
||||
_push(\`<div\${_ssrRenderAttrs(_attrs)}><select>\`)
|
||||
if (true) {
|
||||
_push(\`<option\${
|
||||
_ssrRenderAttr("value", _ctx.i)
|
||||
}\${
|
||||
(_ssrIncludeBooleanAttr((Array.isArray(_ctx.model))
|
||||
? _ssrLooseContain(_ctx.model, _ctx.i)
|
||||
: _ssrLooseEqual(_ctx.model, _ctx.i))) ? " selected" : ""
|
||||
}></option>\`)
|
||||
} else {
|
||||
_push(\`<!---->\`)
|
||||
}
|
||||
_push(\`</select></div>\`)
|
||||
}"
|
||||
`)
|
||||
|
||||
expect(
|
||||
compileWithWrapper(
|
||||
`<select multiple v-model="model"><option value="1" selected></option><option value="2"></option></select>`,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@vue/compiler-ssr",
|
||||
"version": "3.5.0-beta.1",
|
||||
"version": "3.5.13",
|
||||
"description": "@vue/compiler-ssr",
|
||||
"main": "dist/compiler-ssr.cjs.js",
|
||||
"types": "dist/compiler-ssr.d.ts",
|
||||
|
|
|
@ -156,7 +156,7 @@ export function processChildren(
|
|||
context: SSRTransformContext,
|
||||
asFragment = false,
|
||||
disableNestedFragments = false,
|
||||
disableCommentAsIfAlternate = false,
|
||||
disableComment = false,
|
||||
): void {
|
||||
if (asFragment) {
|
||||
context.pushStringPart(`<!--[-->`)
|
||||
|
@ -197,7 +197,9 @@ export function processChildren(
|
|||
case NodeTypes.COMMENT:
|
||||
// no need to escape comment here because the AST can only
|
||||
// contain valid comments.
|
||||
context.pushStringPart(`<!--${child.content}-->`)
|
||||
if (!disableComment) {
|
||||
context.pushStringPart(`<!--${child.content}-->`)
|
||||
}
|
||||
break
|
||||
case NodeTypes.INTERPOLATION:
|
||||
context.pushStringPart(
|
||||
|
@ -207,12 +209,7 @@ export function processChildren(
|
|||
)
|
||||
break
|
||||
case NodeTypes.IF:
|
||||
ssrProcessIf(
|
||||
child,
|
||||
context,
|
||||
disableNestedFragments,
|
||||
disableCommentAsIfAlternate,
|
||||
)
|
||||
ssrProcessIf(child, context, disableNestedFragments, disableComment)
|
||||
break
|
||||
case NodeTypes.FOR:
|
||||
ssrProcessFor(child, context, disableNestedFragments)
|
||||
|
|
|
@ -28,6 +28,7 @@ import {
|
|||
createSequenceExpression,
|
||||
createSimpleExpression,
|
||||
createTemplateLiteral,
|
||||
findDir,
|
||||
hasDynamicKeyVBind,
|
||||
isStaticArgOf,
|
||||
isStaticExp,
|
||||
|
@ -164,24 +165,28 @@ export const ssrTransformElement: NodeTransform = (node, context) => {
|
|||
]
|
||||
}
|
||||
} else if (directives.length && !node.children.length) {
|
||||
const tempId = `_temp${context.temps++}`
|
||||
propsExp.arguments = [
|
||||
createAssignmentExpression(
|
||||
createSimpleExpression(tempId, false),
|
||||
mergedProps,
|
||||
),
|
||||
]
|
||||
rawChildrenMap.set(
|
||||
node,
|
||||
createConditionalExpression(
|
||||
createSimpleExpression(`"textContent" in ${tempId}`, false),
|
||||
createCallExpression(context.helper(SSR_INTERPOLATE), [
|
||||
createSimpleExpression(`${tempId}.textContent`, false),
|
||||
]),
|
||||
createSimpleExpression(`${tempId}.innerHTML ?? ''`, false),
|
||||
false,
|
||||
),
|
||||
)
|
||||
// v-text directive has higher priority than the merged props
|
||||
const vText = findDir(node, 'text')
|
||||
if (!vText) {
|
||||
const tempId = `_temp${context.temps++}`
|
||||
propsExp.arguments = [
|
||||
createAssignmentExpression(
|
||||
createSimpleExpression(tempId, false),
|
||||
mergedProps,
|
||||
),
|
||||
]
|
||||
rawChildrenMap.set(
|
||||
node,
|
||||
createConditionalExpression(
|
||||
createSimpleExpression(`"textContent" in ${tempId}`, false),
|
||||
createCallExpression(context.helper(SSR_INTERPOLATE), [
|
||||
createSimpleExpression(`${tempId}.textContent`, false),
|
||||
]),
|
||||
createSimpleExpression(`${tempId}.innerHTML ?? ''`, false),
|
||||
false,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if (needTagForRuntime) {
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue