mirror of https://github.com/vuejs/core.git
Merge branch 'main' into feat/reactiveity-transform-shorthands
This commit is contained in:
commit
fbbaa0cc3f
|
@ -6,6 +6,7 @@ module.exports = {
|
|||
parserOptions: {
|
||||
sourceType: 'module'
|
||||
},
|
||||
plugins: ["jest"],
|
||||
rules: {
|
||||
'no-debugger': 'error',
|
||||
'no-unused-vars': [
|
||||
|
@ -32,7 +33,9 @@ module.exports = {
|
|||
files: ['**/__tests__/**', 'test-dts/**'],
|
||||
rules: {
|
||||
'no-restricted-globals': 'off',
|
||||
'no-restricted-syntax': 'off'
|
||||
'no-restricted-syntax': 'off',
|
||||
'jest/no-disabled-tests': 'error',
|
||||
'jest/no-focused-tests': 'error'
|
||||
}
|
||||
},
|
||||
// shared, may be used in any env
|
||||
|
|
|
@ -17,6 +17,12 @@ body:
|
|||
Also try to search for your issue - it may have already been answered or even fixed in the development branch.
|
||||
However, if you find that an old, closed issue still persists in the latest version,
|
||||
you should open a new issue using the form below instead of commenting on the old issue.
|
||||
- type: input
|
||||
id: version
|
||||
attributes:
|
||||
label: Vue version
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: reproduction-link
|
||||
attributes:
|
||||
|
|
|
@ -19,6 +19,8 @@ Hi! I'm really excited that you are interested in contributing to Vue.js. Before
|
|||
|
||||
- Checkout a topic branch from a base branch, e.g. `main`, and merge back against that branch.
|
||||
|
||||
- [Make sure to tick the "Allow edits from maintainers" box](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/allowing-changes-to-a-pull-request-branch-created-from-a-fork). This allows us to directly make minor edits / refactors and saves a lot of time.
|
||||
|
||||
- If adding a new feature:
|
||||
|
||||
- Add accompanying test case.
|
||||
|
|
|
@ -62,3 +62,9 @@ updates:
|
|||
- dependency-name: node-notifier
|
||||
versions:
|
||||
- 8.0.1
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: monthly
|
||||
open-pull-requests-limit: 10
|
||||
versioning-strategy: lockfile-only
|
||||
|
|
|
@ -13,9 +13,7 @@ jobs:
|
|||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v2.0.1
|
||||
with:
|
||||
version: 7.0.1
|
||||
uses: pnpm/action-setup@v2
|
||||
|
||||
- name: Set node version to 16
|
||||
uses: actions/setup-node@v2
|
||||
|
@ -34,9 +32,7 @@ jobs:
|
|||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v2.0.1
|
||||
with:
|
||||
version: 7.0.1
|
||||
uses: pnpm/action-setup@v2
|
||||
|
||||
- name: Set node version to 16
|
||||
uses: actions/setup-node@v2
|
||||
|
@ -55,9 +51,7 @@ jobs:
|
|||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v2.0.1
|
||||
with:
|
||||
version: 7.0.1
|
||||
uses: pnpm/action-setup@v2
|
||||
|
||||
- name: Set node version to 16
|
||||
uses: actions/setup-node@v2
|
||||
|
@ -81,9 +75,7 @@ jobs:
|
|||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v2.0.1
|
||||
with:
|
||||
version: 7.0.1
|
||||
uses: pnpm/action-setup@v2
|
||||
|
||||
- name: Set node version to 16
|
||||
uses: actions/setup-node@v2
|
||||
|
|
135
CHANGELOG.md
135
CHANGELOG.md
|
@ -1,3 +1,138 @@
|
|||
## [3.2.37](https://github.com/vuejs/core/compare/v3.2.36...v3.2.37) (2022-06-06)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **compiler-sfc:** improve css v-bind parsing ([e60244b](https://github.com/vuejs/core/commit/e60244bcdf0b386de1560ff7c205ae0870bab355)), closes [#6022](https://github.com/vuejs/core/issues/6022)
|
||||
* **runtime-core:** hydrate Static vnode ([#6015](https://github.com/vuejs/core/issues/6015)) ([11e17a1](https://github.com/vuejs/core/commit/11e17a1a29cf3d0b37628241d63ff3e8d8525f95)), closes [#6008](https://github.com/vuejs/core/issues/6008)
|
||||
* **sfc:** avoid auto name inference leading to unwanted recursion ([9734b31](https://github.com/vuejs/core/commit/9734b31c312244a2b5c5cf83c75d7b34076a0c4b)), closes [#5965](https://github.com/vuejs/core/issues/5965) [#6027](https://github.com/vuejs/core/issues/6027) [#6029](https://github.com/vuejs/core/issues/6029)
|
||||
* **ssr:** ensure app can be unmounted when created with createSSRApp() ([#5992](https://github.com/vuejs/core/issues/5992)) ([d4d3319](https://github.com/vuejs/core/commit/d4d3319c1be16dc9a046b2c5521096debc205f25)), closes [#5990](https://github.com/vuejs/core/issues/5990)
|
||||
* **ssr:** hydration for transition wrapper components with empty slot content ([#5995](https://github.com/vuejs/core/issues/5995)) ([eb22a62](https://github.com/vuejs/core/commit/eb22a62798d845a8756b0a73b68afdd874feda59)), closes [#5991](https://github.com/vuejs/core/issues/5991)
|
||||
|
||||
|
||||
|
||||
## [3.2.36](https://github.com/vuejs/core/compare/v3.2.35...v3.2.36) (2022-05-23)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **compat:** fix app-level asset registration affecting other local apps ([#5979](https://github.com/vuejs/core/issues/5979)) ([7fb5732](https://github.com/vuejs/core/commit/7fb57327b9d0e4d9eb675149f167d915fb0d59fa))
|
||||
* **compat:** fix globalProperties pollution in v3 mode ([2f07e34](https://github.com/vuejs/core/commit/2f07e3460bf51bc1b083f3d03b3d192e042d2d75)), closes [#5699](https://github.com/vuejs/core/issues/5699)
|
||||
* **compiler-core:** fix svg with directives being incorrectly hoisted ([#5919](https://github.com/vuejs/core/issues/5919)) ([7fbc933](https://github.com/vuejs/core/commit/7fbc933f4d80c0259ee24872ba790681cf3cbe76)), closes [#5289](https://github.com/vuejs/core/issues/5289)
|
||||
* **sfc/types:** allow use default factory for primitive types in `withDefaults` ([#5939](https://github.com/vuejs/core/issues/5939)) ([b546282](https://github.com/vuejs/core/commit/b5462822d6c0a43866deef2b3437bbe3bbfb3625)), closes [#5938](https://github.com/vuejs/core/issues/5938)
|
||||
* **transition:** fix cancel leave regression ([#5974](https://github.com/vuejs/core/issues/5974)) ([dddbd96](https://github.com/vuejs/core/commit/dddbd96dfe69292cee401f72d2703e8fb3708a14)), closes [#5973](https://github.com/vuejs/core/issues/5973)
|
||||
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* improve the performance of getNow ([#5944](https://github.com/vuejs/core/issues/5944)) ([3bdc41d](https://github.com/vuejs/core/commit/3bdc41dff305422cb5334a64353c314bce1202a4))
|
||||
|
||||
|
||||
|
||||
## [3.2.35](https://github.com/vuejs/core/compare/v3.2.34...v3.2.35) (2022-05-20)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **compiler-sfc:** fix usage detection for types in v-for/v-slot expressions ([583b625](https://github.com/vuejs/core/commit/583b6259870211c32efee0bb4a60b342799d80f7)), closes [#5959](https://github.com/vuejs/core/issues/5959)
|
||||
* **types:** fix typescript error when spreading `$props`([#5968](https://github.com/vuejs/core/issues/5968)) ([0c7fd13](https://github.com/vuejs/core/commit/0c7fd13ea628a2f1b72c6f4150c0dba32da4468e))
|
||||
* **types:** restore DefineComponent argument order ([8071ef4](https://github.com/vuejs/core/commit/8071ef47b5adcd5fcd9d0d2ea2cefff5c34ce095)), closes [#5416](https://github.com/vuejs/core/issues/5416) [#3796](https://github.com/vuejs/core/issues/3796) [#5967](https://github.com/vuejs/core/issues/5967)
|
||||
|
||||
|
||||
|
||||
## [3.2.34](https://github.com/vuejs/core/compare/v3.2.34-beta.1...v3.2.34) (2022-05-19)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **compiler-core:** should generate HYDRATE_EVENTS flag on dynamic component that resolves to element ([415091b](https://github.com/vuejs/core/commit/415091b0ee2de66e622145028f00523f2032ce77)), closes [#5870](https://github.com/vuejs/core/issues/5870)
|
||||
* **compiler-sfc:** support `export { default } from '...'` ([#5937](https://github.com/vuejs/core/issues/5937)) ([73e6523](https://github.com/vuejs/core/commit/73e6523134a013f9e369f53f213a214497ac7c40)), closes [#5935](https://github.com/vuejs/core/issues/5935)
|
||||
* **compiler-sfc:** type-only defineProps does not recognize Promise (fix [#5941](https://github.com/vuejs/core/issues/5941)) ([#5943](https://github.com/vuejs/core/issues/5943)) ([991d623](https://github.com/vuejs/core/commit/991d62322fa67d50b7ae8b0460f294d6b39f9711))
|
||||
* **compiler-ssr:** fix component event handlers inheritance in ssr ([f811dc2](https://github.com/vuejs/core/commit/f811dc2b60ba7efdbb9b1ab330dcbc18c1cc9a75)), closes [#5664](https://github.com/vuejs/core/issues/5664)
|
||||
* **compiler-ssr:** fix wrong attrs fallthrough on non-single-root v-if branches ([516bc54](https://github.com/vuejs/core/commit/516bc548fce295f6d564c7fb371c8067ead7cd71)), closes [#5140](https://github.com/vuejs/core/issues/5140)
|
||||
* **compiler-ssr:** only inject fallthrough attrs for root transition/keep-alive ([c65b805](https://github.com/vuejs/core/commit/c65b805ef1f9b164fb8aaa7bc679a91248b5891a))
|
||||
* **keep-alive:** fix keep-alive rendering when placed in vnode branch ([0841b9b](https://github.com/vuejs/core/commit/0841b9b5243acdaf191099b25e9a145b30189dea)), closes [#4817](https://github.com/vuejs/core/issues/4817)
|
||||
* **runtime-core:** adjust force diff of dev root fragments ([cdda49b](https://github.com/vuejs/core/commit/cdda49bbfb1939c9cf812d624992ea7bdae74c78)), closes [#5946](https://github.com/vuejs/core/issues/5946)
|
||||
* **ssr/teleport:** support nested teleports in ssr ([595263c](https://github.com/vuejs/core/commit/595263c0e9f5728c3650c6526dbed27cda9ba114)), closes [#5242](https://github.com/vuejs/core/issues/5242)
|
||||
* **ssr:** fix hydration error on falsy v-if inside transition/keep-alive ([ee4186e](https://github.com/vuejs/core/commit/ee4186ef9ebbc45827b208f6f5b648dbf4337d1d)), closes [#5352](https://github.com/vuejs/core/issues/5352)
|
||||
* **ssr:** fix hydration error when teleport is used as component root ([b60cff0](https://github.com/vuejs/core/commit/b60cff052c880b2965d06007f0ec4d0349ab47c0)), closes [#4293](https://github.com/vuejs/core/issues/4293)
|
||||
* **ssr:** fix hydration error when transition contains comment children ([3705b3b](https://github.com/vuejs/core/commit/3705b3b46aa8f3e929014f564f8afa4a663e6375)), closes [#5351](https://github.com/vuejs/core/issues/5351)
|
||||
* **ssr:** fix hydration for slot with empty text node ([939209c](https://github.com/vuejs/core/commit/939209c6b554aed6634d9cf2ca10a2aa46ba7673)), closes [#5728](https://github.com/vuejs/core/issues/5728)
|
||||
* **ssr:** fix hydration mismatch caused by multi-line comments inside slot ([e1bc268](https://github.com/vuejs/core/commit/e1bc2681ef64aed7975ad38950a478ae53c1abad)), closes [#5355](https://github.com/vuejs/core/issues/5355)
|
||||
* **ssr:** inherit scope id on functional component during ssr ([847d7f7](https://github.com/vuejs/core/commit/847d7f782bb6074c6b31378e07d94cb41ad30bd2)), closes [#5817](https://github.com/vuejs/core/issues/5817)
|
||||
* **ssr:** render fallthrough attributes for transition-group with tag ([aed10c5](https://github.com/vuejs/core/commit/aed10c507279900f8afc4861dc01ca4f2b95acb8)), closes [#5141](https://github.com/vuejs/core/issues/5141)
|
||||
* **ssr:** support client-compiled v-model with dynamic type during ssr ([#5787](https://github.com/vuejs/core/issues/5787)) ([c03459b](https://github.com/vuejs/core/commit/c03459b9b6d3c18450235bc4074a603677996320)), closes [#5786](https://github.com/vuejs/core/issues/5786)
|
||||
* **types:** export ComponentProvideOptions ([#5947](https://github.com/vuejs/core/issues/5947)) ([3e2850f](https://github.com/vuejs/core/commit/3e2850fa6c628284b4a1ab5deba3b11f1d2f66b6))
|
||||
* **types:** fix `defineComponent` inference to `Component` ([#5949](https://github.com/vuejs/core/issues/5949)) ([7c8f457](https://github.com/vuejs/core/commit/7c8f4578e9e7178e326cf8e343f7a8b4143ba63b))
|
||||
|
||||
|
||||
|
||||
## [3.2.34-beta.1](https://github.com/vuejs/core/compare/v3.2.33...v3.2.34-beta.1) (2022-05-17)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **compiler-core:** normalize v-bind:style with array literal value ([0f00cf4](https://github.com/vuejs/core/commit/0f00cf43cf5eaeeee7f12d523a5f4936f7dc0a84)), closes [#5106](https://github.com/vuejs/core/issues/5106)
|
||||
* **compiler-core:** template v-if should never be treated as dev root fragment ([51f3d38](https://github.com/vuejs/core/commit/51f3d386de7f5fcec6eb4c1c223ba824be036648)), closes [#5189](https://github.com/vuejs/core/issues/5189)
|
||||
* **compiler-dom:** properly stringify v-html/v-text with constant value ([6283b2e](https://github.com/vuejs/core/commit/6283b2ec413b78fe88775d249d3598cdce977b7a)), closes [#5439](https://github.com/vuejs/core/issues/5439) [#5445](https://github.com/vuejs/core/issues/5445)
|
||||
* **compiler-sfc:** `<script>` after `<script setup>` the script content not end with `\\n` ([3b7b107](https://github.com/vuejs/core/commit/3b7b107120c6dba70b068312afd594c3575ea9eb))
|
||||
* **compiler-sfc:** add test for [#5808](https://github.com/vuejs/core/issues/5808) ([a0290fe](https://github.com/vuejs/core/commit/a0290fe781a0ab2e90239d615d18fdb7ee37cdfe))
|
||||
* **compiler-sfc:** async transformer doesn't correctly detect need for semicolon in block [#5808](https://github.com/vuejs/core/issues/5808) ([6c3b681](https://github.com/vuejs/core/commit/6c3b681d235bc70293827c535572f90be1ab6c68))
|
||||
* **compiler-sfc:** automatically infer component name from filename when using script setup ([#4997](https://github.com/vuejs/core/issues/4997)) ([1693924](https://github.com/vuejs/core/commit/16939241b0f64cefea254b024a9b5a25caea93d9)), closes [#4993](https://github.com/vuejs/core/issues/4993)
|
||||
* **compiler-sfc:** defineProps return binding or rest binding should be considered reactive ([4101441](https://github.com/vuejs/core/commit/410144149fbaaecad2b2d36a9cfe965ab7b2b6e6))
|
||||
* **compiler-sfc:** ensure consistent behavior of export default render with script setup ([b7025d2](https://github.com/vuejs/core/commit/b7025d24f1c33023d020d60292319740852d1810)), closes [#4980](https://github.com/vuejs/core/issues/4980)
|
||||
* **compiler-sfc:** fix defineEmits() scope reference check error message ([#5404](https://github.com/vuejs/core/issues/5404)) ([f2c48f5](https://github.com/vuejs/core/commit/f2c48f535250d01133a5e49c0bf2c26a46b6d935))
|
||||
* **compiler-sfc:** fix object default values for reactive props destructure ([7dfe146](https://github.com/vuejs/core/commit/7dfe146096487a98ba1733598c44407bc89a1b9f))
|
||||
* **compiler-sfc:** fix skipped srcset transform when using base option ([41d255b](https://github.com/vuejs/core/commit/41d255ba5ddd40f1a1dd2dd6add01f38e20d6325)), closes [#4835](https://github.com/vuejs/core/issues/4835) [#4819](https://github.com/vuejs/core/issues/4819) [#4834](https://github.com/vuejs/core/issues/4834) [#4835](https://github.com/vuejs/core/issues/4835)
|
||||
* **compiler-sfc:** fix template usage check false positives on types ([ccf9256](https://github.com/vuejs/core/commit/ccf92564d339cdee3947aecfba2e861c88864883)), closes [#5414](https://github.com/vuejs/core/issues/5414)
|
||||
* **compiler-sfc:** fix treeshaking of namespace import when used in template ([8a123ac](https://github.com/vuejs/core/commit/8a123ac34fd30fc36ac9e0c252dd345d6d7c7f50)), closes [#5209](https://github.com/vuejs/core/issues/5209)
|
||||
* **compiler-sfc:** remove the jsx from the babelParserPlugins when not match the case of adding jsx ([#5846](https://github.com/vuejs/core/issues/5846)) ([7d7a241](https://github.com/vuejs/core/commit/7d7a2410e58d3ae59ca3fcf619f332699209fc96)), closes [#5845](https://github.com/vuejs/core/issues/5845)
|
||||
* **keep-alive:** fix unmounting late-included components ([da49c86](https://github.com/vuejs/core/commit/da49c863a21fb9d9de4a1b816dcc4faff8046fdb)), closes [#3648](https://github.com/vuejs/core/issues/3648) [#3650](https://github.com/vuejs/core/issues/3650)
|
||||
* **keep-alive:** invoke initial activated hook for async components ([20ed16f](https://github.com/vuejs/core/commit/20ed16f68c632a271d4e8cb09087c7ed4e936637)), closes [#5459](https://github.com/vuejs/core/issues/5459) [#5095](https://github.com/vuejs/core/issues/5095) [#5651](https://github.com/vuejs/core/issues/5651)
|
||||
* **reactivity-transform:** fix props access codegen for non-identifier prop names ([#5436](https://github.com/vuejs/core/issues/5436)) ([242914d](https://github.com/vuejs/core/commit/242914d938fccad1c09a52a7ed09f15ac509cf6b)), closes [#5425](https://github.com/vuejs/core/issues/5425)
|
||||
* **reactivity:** ensure computed is invalidated before other effects ([82bdf86](https://github.com/vuejs/core/commit/82bdf8625475e81c44f0d4db4061b882d2fe7612)), closes [#5720](https://github.com/vuejs/core/issues/5720)
|
||||
* **reactivity:** ios10.x compatibility ([#4900](https://github.com/vuejs/core/issues/4900)) ([b87dc06](https://github.com/vuejs/core/commit/b87dc061930e62c78b4cc9a79f385e5880cdf8ae))
|
||||
* **runtime-core:** clone root vnode before inheriting directives ([d36ca4d](https://github.com/vuejs/core/commit/d36ca4d80e297056f6c78cd7e3bc2004e0b58660))
|
||||
* **runtime-core:** ensure consistent behavior between dev/prod on invalid v-for range ([67099fe](https://github.com/vuejs/core/commit/67099fe20299a51f9974f0e2f9ef19ca05efe92b)), closes [#5867](https://github.com/vuejs/core/issues/5867)
|
||||
* **runtime-core:** ensure consistent identity of $forceUpdate and $nextTick instance methods ([d52907f](https://github.com/vuejs/core/commit/d52907f4ebc9bcfd7b3141fbebecd4c5b19aa80f)), closes [#5556](https://github.com/vuejs/core/issues/5556)
|
||||
* **runtime-core:** ensure raw slot function is only normalized once ([#5358](https://github.com/vuejs/core/issues/5358)) ([e4dffe9](https://github.com/vuejs/core/commit/e4dffe900a7475c9b4c22c06283b5635e5c2de37)), closes [#5343](https://github.com/vuejs/core/issues/5343)
|
||||
* **runtime-core:** fix activated hook when using async component with KeepAlive ([#5459](https://github.com/vuejs/core/issues/5459)) ([f1d1cdb](https://github.com/vuejs/core/commit/f1d1cdbb699e27ac6a0d241209f7e8f8d9ebf2c7)), closes [#5095](https://github.com/vuejs/core/issues/5095) [#5651](https://github.com/vuejs/core/issues/5651)
|
||||
* **runtime-core:** fix directive inheritance on dev root fragment ([2bab639](https://github.com/vuejs/core/commit/2bab63968355995a4026cf5cd1ccad7c79e8d89c)), closes [#5523](https://github.com/vuejs/core/issues/5523)
|
||||
* **runtime-core:** fix missed updates when passing vnode to <component :is> ([ba17792](https://github.com/vuejs/core/commit/ba17792b7248552ea01a1ca6eca7d49b47d827f0)), closes [#4903](https://github.com/vuejs/core/issues/4903)
|
||||
* **runtime-core:** handle NaN identity check in v-memo ([#5852](https://github.com/vuejs/core/issues/5852)) ([a388129](https://github.com/vuejs/core/commit/a3881299e98de6217f05bbd42ac509e8cc8be3d9)), closes [#5853](https://github.com/vuejs/core/issues/5853)
|
||||
* **runtime-core:** transition hooks can be arrays of functions ([#5177](https://github.com/vuejs/core/issues/5177)) ([fec12d7](https://github.com/vuejs/core/commit/fec12d7dccc573c016ee2d8e1fa2b67f134c2786))
|
||||
* **runtime-dom:** "el._assign is not a function" in compat mode ([#4121](https://github.com/vuejs/core/issues/4121)) ([e58277f](https://github.com/vuejs/core/commit/e58277f6eaeaec84cf05b34126bec01b619a1b90))
|
||||
* **sfc-playground:** default selected app ([#5370](https://github.com/vuejs/core/issues/5370)) ([04fff05](https://github.com/vuejs/core/commit/04fff05f0043adb753928e806f9d8fb604701a38))
|
||||
* **shared:** improve isDate check ([#5803](https://github.com/vuejs/core/issues/5803)) ([eef1447](https://github.com/vuejs/core/commit/eef14471b228e8f3d3c921a0451d268f81a0a74d))
|
||||
* **shared:** missed Symbol judge in looseEqual ([#3553](https://github.com/vuejs/core/issues/3553)) ([0aeb4bc](https://github.com/vuejs/core/commit/0aeb4bc9bf68c0006b496142bb5aeb3f06689b7c))
|
||||
* **ssr/sfc-css-vars:** fix v-bind css vars codegen for SSR ([efea4a8](https://github.com/vuejs/core/commit/efea4a8b5784f4660836b124d16dc29f7fea41e4)), closes [#5443](https://github.com/vuejs/core/issues/5443) [#5444](https://github.com/vuejs/core/issues/5444)
|
||||
* **ssr:** don't warn for missing teleport target if disabled ([#5135](https://github.com/vuejs/core/issues/5135)) ([da10dd7](https://github.com/vuejs/core/commit/da10dd7de91123666152d058f3c18da3e9e7f22a))
|
||||
* **ssr:** fix hydration error for slot outlet inside transition ([9309b04](https://github.com/vuejs/core/commit/9309b044bd4f9d0a34e0d702ed4690a529443a41)), closes [#3989](https://github.com/vuejs/core/issues/3989)
|
||||
* **ssr:** fix ssr render output for fragment in slots ([70c2d5b](https://github.com/vuejs/core/commit/70c2d5bbc066585febd12536a6ab39c4384b277b)), closes [#5859](https://github.com/vuejs/core/issues/5859)
|
||||
* **ssr:** implement empty read() on node stream ([c355c4b](https://github.com/vuejs/core/commit/c355c4b78451708b04e17a7c50680dee507f0c40)), closes [#3846](https://github.com/vuejs/core/issues/3846) [#3867](https://github.com/vuejs/core/issues/3867)
|
||||
* **ssr:** render teleport inside async component ([#5187](https://github.com/vuejs/core/issues/5187)) ([4d7803e](https://github.com/vuejs/core/commit/4d7803ed28fb67d45a83d3500f5407754e65bf64))
|
||||
* **ssr:** resolve teleports for stream render APIs ([77fef97](https://github.com/vuejs/core/commit/77fef9734496abe1e842d8e4086c497656d1fab2))
|
||||
* **ssr:** should de-optimize on vnode with PatchFlags.BAIL ([#4818](https://github.com/vuejs/core/issues/4818)) ([cd659fc](https://github.com/vuejs/core/commit/cd659fc86f74741987bacc351f0d94ef3b80a1ca)), closes [#4679](https://github.com/vuejs/core/issues/4679) [#5771](https://github.com/vuejs/core/issues/5771)
|
||||
* **ssr:** should not hoist transformed asset urls in ssr compile ([57bb37b](https://github.com/vuejs/core/commit/57bb37bd64cc6b2aa3099d91585441db6d43e2a2)), closes [#3874](https://github.com/vuejs/core/issues/3874)
|
||||
* **transition/v-show:** ensure transition is in persisted mode when used with v-show ([425310e](https://github.com/vuejs/core/commit/425310e8b614ad91660dd93d4e7905fbe572ef31)), closes [#4845](https://github.com/vuejs/core/issues/4845) [#4852](https://github.com/vuejs/core/issues/4852)
|
||||
* **transition:** handle edge case of cancel leave before next frame ([59cf295](https://github.com/vuejs/core/commit/59cf2958e7bae5f1e13953373fcdbb4ad988be6c)), closes [#4462](https://github.com/vuejs/core/issues/4462)
|
||||
* **types:** add `Set<any>` to checkbox binding type for v-model ([#5713](https://github.com/vuejs/core/issues/5713)) ([e5a9089](https://github.com/vuejs/core/commit/e5a90893a6a25a40464cc9d5484093ec531e8b78))
|
||||
* **types:** allow css variables in style binding ([#5542](https://github.com/vuejs/core/issues/5542)) ([9def7aa](https://github.com/vuejs/core/commit/9def7aa50827b12fdfbef28e5db6f373d5cba279))
|
||||
* **types:** allow indeterminate for checkbox ([#3473](https://github.com/vuejs/core/issues/3473)) ([d4fcfdd](https://github.com/vuejs/core/commit/d4fcfddec6170cc1528a285fc8e0b3f3c5590311))
|
||||
* **types:** keep the original type when unwrapping `markRaw` ([#3791](https://github.com/vuejs/core/issues/3791)) ([32e53bf](https://github.com/vuejs/core/commit/32e53bfd478af895dd090ea6c8766fa043e179e4))
|
||||
* **types:** preserve and expose original options on defineComponent return type ([#5416](https://github.com/vuejs/core/issues/5416)) ([98b821d](https://github.com/vuejs/core/commit/98b821d94a0a0fb4d7701809da6bec331a47e6e5)), closes [#3796](https://github.com/vuejs/core/issues/3796)
|
||||
* **v-model:** exclude range from lazy guard logic ([8c51c65](https://github.com/vuejs/core/commit/8c51c6514f99b4c70183f4c1a0eaabd482f88d5b)), closes [#5875](https://github.com/vuejs/core/issues/5875)
|
||||
* **v-model:** fix case where .trim and .number modifiers are used together ([#5842](https://github.com/vuejs/core/issues/5842)) ([71066b5](https://github.com/vuejs/core/commit/71066b5afed7d3707b8ec9a6313dbdbd1adad45e)), closes [#5839](https://github.com/vuejs/core/issues/5839)
|
||||
* **watch:** fix flush: pre watchers triggered synchronously in setup ([74d2a76](https://github.com/vuejs/core/commit/74d2a76af6e830af5abb8aac8484dc1b3e90a510)), closes [#5721](https://github.com/vuejs/core/issues/5721)
|
||||
* **watch:** fix watching multiple sources containing shallowRef ([#5381](https://github.com/vuejs/core/issues/5381)) ([220f255](https://github.com/vuejs/core/commit/220f255fe94d5f1d2ccf3af3a469e9328c916167)), closes [#5371](https://github.com/vuejs/core/issues/5371)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **types:** avoid props JSDocs loss by `default` option ([#5871](https://github.com/vuejs/core/issues/5871)) ([c901dca](https://github.com/vuejs/core/commit/c901dca5add2c32554403e5896247fdb8aa7cf7d))
|
||||
|
||||
|
||||
|
||||
## [3.2.33](https://github.com/vuejs/core/compare/v3.2.32...v3.2.33) (2022-04-14)
|
||||
|
||||
|
||||
|
|
13
package.json
13
package.json
|
@ -1,13 +1,14 @@
|
|||
{
|
||||
"private": true,
|
||||
"version": "3.2.33",
|
||||
"version": "3.2.37",
|
||||
"packageManager": "pnpm@7.1.0",
|
||||
"scripts": {
|
||||
"dev": "node scripts/dev.js",
|
||||
"build": "node scripts/build.js",
|
||||
"size": "run-s size-global size-baseline",
|
||||
"size-global": "node scripts/build.js vue runtime-dom -f global -p",
|
||||
"size-baseline": "node scripts/build.js runtime-dom runtime-core reactivity shared -f esm-bundler && cd packages/size-check && vite build && node brotli",
|
||||
"lint": "eslint --ext .ts packages/*/src/**.ts",
|
||||
"lint": "eslint --ext .ts packages/*/{src,__tests__}/**.ts",
|
||||
"format": "prettier --write --parser typescript \"packages/**/*.ts?(x)\"",
|
||||
"test": "run-s \"test-unit {@}\" \"test-e2e {@}\"",
|
||||
"test-unit": "jest --filter ./scripts/filter-unit.js",
|
||||
|
@ -17,13 +18,14 @@
|
|||
"test-coverage": "node scripts/build.js vue -f global -d && jest --runInBand --coverage --bail",
|
||||
"release": "node scripts/release.js",
|
||||
"changelog": "conventional-changelog -p angular -i CHANGELOG.md -s",
|
||||
"dev-esm": "node scripts/dev.js -if esm-bundler-runtime",
|
||||
"dev-compiler": "run-p \"dev template-explorer\" serve",
|
||||
"dev-sfc": "run-p \"dev compiler-sfc -f esm-browser\" \"dev vue -if esm-bundler-runtime \" serve-sfc-playground",
|
||||
"dev-sfc": "run-p \"dev compiler-sfc -f esm-browser\" \"dev vue -if esm-bundler-runtime\" \"dev server-renderer -if esm-bundler\" serve-sfc-playground",
|
||||
"serve-sfc-playground": "vite packages/sfc-playground --host",
|
||||
"serve": "serve",
|
||||
"open": "open http://localhost:5000/packages/template-explorer/local.html",
|
||||
"preinstall": "node ./scripts/preinstall.js",
|
||||
"prebuild-sfc-playground": "node scripts/build.js compiler reactivity-transform shared -af cjs && 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 && node scripts/build.js compiler-sfc -f esm-browser",
|
||||
"prebuild-sfc-playground": "node scripts/build.js compiler reactivity-transform shared -af cjs && 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 && node scripts/build.js compiler-sfc server-renderer -f esm-browser",
|
||||
"build-sfc-playground": "cd packages/sfc-playground && npm run build"
|
||||
},
|
||||
"types": "test-dts/index.d.ts",
|
||||
|
@ -44,7 +46,7 @@
|
|||
]
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16.5.0"
|
||||
"node": ">=16.11.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/types": "^7.12.0",
|
||||
|
@ -69,6 +71,7 @@
|
|||
"enquirer": "^2.3.2",
|
||||
"esbuild": "^0.14.35",
|
||||
"eslint": "^7.7.0",
|
||||
"eslint-plugin-jest": "26.1.5",
|
||||
"execa": "^4.0.2",
|
||||
"fs-extra": "^9.0.1",
|
||||
"jest": "^27.1.0",
|
||||
|
|
|
@ -211,6 +211,30 @@ return function render(_ctx, _cache) {
|
|||
}"
|
||||
`;
|
||||
|
||||
exports[`compiler: hoistStatic transform prefixIdentifiers should NOT hoist SVG with directives 1`] = `
|
||||
"const _Vue = Vue
|
||||
const { createElementVNode: _createElementVNode } = _Vue
|
||||
|
||||
const _hoisted_1 = /*#__PURE__*/_createElementVNode(\\"path\\", { d: \\"M2,3H5.5L12\\" }, null, -1 /* HOISTED */)
|
||||
const _hoisted_2 = [
|
||||
_hoisted_1
|
||||
]
|
||||
|
||||
return function render(_ctx, _cache) {
|
||||
with (_ctx) {
|
||||
const { createElementVNode: _createElementVNode, resolveDirective: _resolveDirective, openBlock: _openBlock, createElementBlock: _createElementBlock, withDirectives: _withDirectives } = _Vue
|
||||
|
||||
const _directive_foo = _resolveDirective(\\"foo\\")
|
||||
|
||||
return (_openBlock(), _createElementBlock(\\"div\\", null, [
|
||||
_withDirectives((_openBlock(), _createElementBlock(\\"svg\\", null, _hoisted_2)), [
|
||||
[_directive_foo]
|
||||
])
|
||||
]))
|
||||
}
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`compiler: hoistStatic transform prefixIdentifiers should NOT hoist elements with cached handlers + other bindings 1`] = `
|
||||
"import { normalizeClass as _normalizeClass, createElementVNode as _createElementVNode, openBlock as _openBlock, createElementBlock as _createElementBlock } from \\"vue\\"
|
||||
|
||||
|
|
|
@ -616,5 +616,11 @@ describe('compiler: hoistStatic transform', () => {
|
|||
expect(root.hoists.length).toBe(0)
|
||||
expect(generate(root).code).toMatchSnapshot()
|
||||
})
|
||||
|
||||
test('should NOT hoist SVG with directives', () => {
|
||||
const root = transformWithHoist(`<div><svg v-foo><path d="M2,3H5.5L12"/></svg></div>`)
|
||||
expect(root.hoists.length).toBe(2)
|
||||
expect(generate(root).code).toMatchSnapshot()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
@ -1053,6 +1053,21 @@ describe('compiler: element transform', () => {
|
|||
genFlagText([PatchFlags.PROPS, PatchFlags.HYDRATE_EVENTS])
|
||||
)
|
||||
})
|
||||
|
||||
// #5870
|
||||
test('HYDRATE_EVENTS on dynamic component', () => {
|
||||
const { node } = parseWithElementTransform(
|
||||
`<component :is="foo" @input="foo" />`,
|
||||
{
|
||||
directiveTransforms: {
|
||||
on: transformOn
|
||||
}
|
||||
}
|
||||
)
|
||||
expect(node.patchFlag).toBe(
|
||||
genFlagText([PatchFlags.PROPS, PatchFlags.HYDRATE_EVENTS])
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('dynamic component', () => {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@vue/compiler-core",
|
||||
"version": "3.2.33",
|
||||
"version": "3.2.37",
|
||||
"description": "@vue/compiler-core",
|
||||
"main": "index.js",
|
||||
"module": "dist/compiler-core.esm-bundler.js",
|
||||
|
@ -32,7 +32,7 @@
|
|||
},
|
||||
"homepage": "https://github.com/vuejs/core/tree/main/packages/compiler-core#readme",
|
||||
"dependencies": {
|
||||
"@vue/shared": "3.2.33",
|
||||
"@vue/shared": "3.2.37",
|
||||
"@babel/parser": "^7.16.4",
|
||||
"estree-walker": "^2.0.2",
|
||||
"source-map": "^0.6.1"
|
||||
|
|
|
@ -68,7 +68,7 @@ export interface DirectiveTransformResult {
|
|||
ssrTagParts?: TemplateLiteral['elements']
|
||||
}
|
||||
|
||||
// A structural directive transform is a technically a NodeTransform;
|
||||
// A structural directive transform is technically also a NodeTransform;
|
||||
// Only v-if and v-for fall into this category.
|
||||
export type StructuralDirectiveTransform = (
|
||||
node: ElementNode,
|
||||
|
|
|
@ -230,6 +230,15 @@ export function getConstantType(
|
|||
// static then they don't need to be blocks since there will be no
|
||||
// nested updates.
|
||||
if (codegenNode.isBlock) {
|
||||
// except set custom directives.
|
||||
for (let i = 0; i < node.props.length; i++) {
|
||||
const p = node.props[i]
|
||||
if (p.type === NodeTypes.DIRECTIVE) {
|
||||
constantCache.set(node, ConstantTypes.NOT_CONSTANT)
|
||||
return ConstantTypes.NOT_CONSTANT
|
||||
}
|
||||
}
|
||||
|
||||
context.removeHelper(OPEN_BLOCK)
|
||||
context.removeHelper(
|
||||
getVNodeBlockHelper(context.inSSR, codegenNode.isComponent)
|
||||
|
|
|
@ -121,7 +121,13 @@ export const transformElement: NodeTransform = (node, context) => {
|
|||
|
||||
// props
|
||||
if (props.length > 0) {
|
||||
const propsBuildResult = buildProps(node, context)
|
||||
const propsBuildResult = buildProps(
|
||||
node,
|
||||
context,
|
||||
undefined,
|
||||
isComponent,
|
||||
isDynamicComponent
|
||||
)
|
||||
vnodeProps = propsBuildResult.props
|
||||
patchFlag = propsBuildResult.patchFlag
|
||||
dynamicPropNames = propsBuildResult.dynamicPropNames
|
||||
|
@ -380,6 +386,8 @@ export function buildProps(
|
|||
node: ElementNode,
|
||||
context: TransformContext,
|
||||
props: ElementNode['props'] = node.props,
|
||||
isComponent: boolean,
|
||||
isDynamicComponent: boolean,
|
||||
ssr = false
|
||||
): {
|
||||
props: PropsExpression | undefined
|
||||
|
@ -389,7 +397,6 @@ export function buildProps(
|
|||
shouldUseBlock: boolean
|
||||
} {
|
||||
const { tag, loc: elementLoc, children } = node
|
||||
const isComponent = node.tagType === ElementTypes.COMPONENT
|
||||
let properties: ObjectExpression['properties'] = []
|
||||
const mergeArgs: PropsExpression[] = []
|
||||
const runtimeDirectives: DirectiveNode[] = []
|
||||
|
@ -411,8 +418,8 @@ export function buildProps(
|
|||
const name = key.content
|
||||
const isEventHandler = isOn(name)
|
||||
if (
|
||||
!isComponent &&
|
||||
isEventHandler &&
|
||||
(!isComponent || isDynamicComponent) &&
|
||||
// omit the flag for click handlers because hydration gives click
|
||||
// dedicated fast path.
|
||||
name.toLowerCase() !== 'onclick' &&
|
||||
|
|
|
@ -87,7 +87,13 @@ export function processSlotOutlet(
|
|||
}
|
||||
|
||||
if (nonNameProps.length > 0) {
|
||||
const { props, directives } = buildProps(node, context, nonNameProps)
|
||||
const { props, directives } = buildProps(
|
||||
node,
|
||||
context,
|
||||
nonNameProps,
|
||||
false,
|
||||
false
|
||||
)
|
||||
slotProps = props
|
||||
|
||||
if (directives.length) {
|
||||
|
|
|
@ -5,7 +5,8 @@ import {
|
|||
createCallExpression,
|
||||
CallExpression,
|
||||
ElementTypes,
|
||||
ConstantTypes
|
||||
ConstantTypes,
|
||||
createCompoundExpression
|
||||
} from '../ast'
|
||||
import { isText } from '../utils'
|
||||
import { CREATE_TEXT } from '../runtimeHelpers'
|
||||
|
@ -36,11 +37,10 @@ export const transformText: NodeTransform = (node, context) => {
|
|||
const next = children[j]
|
||||
if (isText(next)) {
|
||||
if (!currentContainer) {
|
||||
currentContainer = children[i] = {
|
||||
type: NodeTypes.COMPOUND_EXPRESSION,
|
||||
loc: child.loc,
|
||||
children: [child]
|
||||
}
|
||||
currentContainer = children[i] = createCompoundExpression(
|
||||
[child],
|
||||
child.loc
|
||||
)
|
||||
}
|
||||
// merge adjacent text node into current
|
||||
currentContainer.children.push(` + `, next)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@vue/compiler-dom",
|
||||
"version": "3.2.33",
|
||||
"version": "3.2.37",
|
||||
"description": "@vue/compiler-dom",
|
||||
"main": "index.js",
|
||||
"module": "dist/compiler-dom.esm-bundler.js",
|
||||
|
@ -37,7 +37,7 @@
|
|||
},
|
||||
"homepage": "https://github.com/vuejs/core/tree/main/packages/compiler-dom#readme",
|
||||
"dependencies": {
|
||||
"@vue/shared": "3.2.33",
|
||||
"@vue/compiler-core": "3.2.33"
|
||||
"@vue/shared": "3.2.37",
|
||||
"@vue/compiler-core": "3.2.37"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
exports[`SFC analyze <script> bindings auto name inference basic 1`] = `
|
||||
"export default {
|
||||
name: 'FooBar',
|
||||
__name: 'FooBar',
|
||||
setup(__props, { expose }) {
|
||||
expose();
|
||||
const a = 1
|
||||
|
@ -205,7 +205,7 @@ export default {
|
|||
expose();
|
||||
|
||||
let __temp, __restore
|
||||
if (ok) {
|
||||
if (ok) {
|
||||
for (let a of [1,2,3]) {
|
||||
(
|
||||
([__temp,__restore] = _withAsyncContext(() => a)),
|
||||
|
@ -240,7 +240,7 @@ export default {
|
|||
expose();
|
||||
|
||||
let __temp, __restore
|
||||
if (ok) {
|
||||
if (ok) {
|
||||
while (d) {
|
||||
(
|
||||
([__temp,__restore] = _withAsyncContext(() => 5)),
|
||||
|
@ -295,7 +295,7 @@ export default {
|
|||
expose();
|
||||
|
||||
let __temp, __restore
|
||||
if (ok) {
|
||||
if (ok) {
|
||||
let a = 'foo'
|
||||
;(
|
||||
([__temp,__restore] = _withAsyncContext(() => 0)),
|
||||
|
@ -351,7 +351,7 @@ if (ok) {
|
|||
__restore()
|
||||
)
|
||||
}
|
||||
} else {
|
||||
} else {
|
||||
(
|
||||
([__temp,__restore] = _withAsyncContext(() => 5)),
|
||||
await __temp,
|
||||
|
@ -722,7 +722,7 @@ return { props, a, emit }
|
|||
|
||||
exports[`SFC compile <script setup> dev mode import usage check TS annotations 1`] = `
|
||||
"import { defineComponent as _defineComponent } from 'vue'
|
||||
import { Foo, Bar, Baz } from './x'
|
||||
import { Foo, Bar, Baz, Qux, Fred } from './x'
|
||||
|
||||
export default /*#__PURE__*/_defineComponent({
|
||||
setup(__props, { expose }) {
|
||||
|
@ -1039,8 +1039,7 @@ return (_ctx, _cache) => {
|
|||
`;
|
||||
|
||||
exports[`SFC compile <script setup> inlineTemplate mode ssr codegen 1`] = `
|
||||
"import { useCssVars as _useCssVars, unref as _unref } from 'vue'
|
||||
import { ssrRenderAttrs as _ssrRenderAttrs, ssrInterpolate as _ssrInterpolate } from \\"vue/server-renderer\\"
|
||||
"import { ssrRenderAttrs as _ssrRenderAttrs, ssrInterpolate as _ssrInterpolate } from \\"vue/server-renderer\\"
|
||||
|
||||
import { ref } from 'vue'
|
||||
|
||||
|
@ -1048,15 +1047,11 @@ export default {
|
|||
__ssrInlineRender: true,
|
||||
setup(__props) {
|
||||
|
||||
_useCssVars(_ctx => ({
|
||||
\\"xxxxxxxx-count\\": (count.value)
|
||||
}))
|
||||
|
||||
const count = ref(0)
|
||||
|
||||
return (_ctx, _push, _parent, _attrs) => {
|
||||
const _cssVars = { style: {
|
||||
\\"xxxxxxxx-count\\": (count.value)
|
||||
\\"--xxxxxxxx-count\\": (count.value)
|
||||
}}
|
||||
_push(\`<!--[--><div\${
|
||||
_ssrRenderAttrs(_cssVars)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { BindingTypes } from '@vue/compiler-core'
|
||||
import { compileSFCScript as compile, assertCode } from './utils'
|
||||
import { compileSFCScript as compile, assertCode, mockId } from './utils'
|
||||
|
||||
describe('SFC compile <script setup>', () => {
|
||||
test('should expose top level declarations', () => {
|
||||
|
@ -168,7 +168,7 @@ defineExpose({ foo: 123 })
|
|||
expect(content).toMatch(/\bexpose\(\{ foo: 123 \}\)/)
|
||||
})
|
||||
|
||||
test('<script> after <script setup> the script content not end with `\\n`',() => {
|
||||
test('<script> after <script setup> the script content not end with `\\n`', () => {
|
||||
const { content } = compile(`
|
||||
<script setup>
|
||||
import { x } from './x'
|
||||
|
@ -446,7 +446,7 @@ defineExpose({ foo: 123 })
|
|||
test('TS annotations', () => {
|
||||
const { content } = compile(`
|
||||
<script setup lang="ts">
|
||||
import { Foo, Bar, Baz } from './x'
|
||||
import { Foo, Bar, Baz, Qux, Fred } from './x'
|
||||
const a = 1
|
||||
function b() {}
|
||||
</script>
|
||||
|
@ -454,11 +454,26 @@ defineExpose({ foo: 123 })
|
|||
{{ a as Foo }}
|
||||
{{ b<Bar>() }}
|
||||
{{ Baz }}
|
||||
<Comp v-slot="{ data }: Qux">{{ data }}</Comp>
|
||||
<div v-for="{ z = x as Qux } in list as Fred"/>
|
||||
</template>
|
||||
`)
|
||||
expect(content).toMatch(`return { a, b, Baz }`)
|
||||
assertCode(content)
|
||||
})
|
||||
|
||||
// vuejs/vue#12591
|
||||
test('v-on inline statement', () => {
|
||||
// should not error
|
||||
compile(`
|
||||
<script setup lang="ts">
|
||||
import { foo } from './foo'
|
||||
</script>
|
||||
<template>
|
||||
<div @click="$emit('update:a');"></div>
|
||||
</tempalte>
|
||||
`)
|
||||
})
|
||||
})
|
||||
|
||||
describe('inlineTemplate mode', () => {
|
||||
|
@ -726,6 +741,8 @@ defineExpose({ foo: 123 })
|
|||
expect(content).toMatch(`\n __ssrInlineRender: true,\n`)
|
||||
expect(content).toMatch(`return (_ctx, _push`)
|
||||
expect(content).toMatch(`ssrInterpolate`)
|
||||
expect(content).not.toMatch(`useCssVars`)
|
||||
expect(content).toMatch(`"--${mockId}-count": (count.value)`)
|
||||
assertCode(content)
|
||||
})
|
||||
})
|
||||
|
@ -1196,7 +1213,7 @@ const emit = defineEmits(['a', 'b'])
|
|||
})
|
||||
|
||||
test('multiple `if` nested statements', () => {
|
||||
assertAwaitDetection(`if (ok) {
|
||||
assertAwaitDetection(`if (ok) {
|
||||
let a = 'foo'
|
||||
await 0 + await 1
|
||||
await 2
|
||||
|
@ -1212,13 +1229,13 @@ const emit = defineEmits(['a', 'b'])
|
|||
await 3
|
||||
await 4
|
||||
}
|
||||
} else {
|
||||
} else {
|
||||
await 5
|
||||
}`)
|
||||
})
|
||||
|
||||
test('multiple `if while` nested statements', () => {
|
||||
assertAwaitDetection(`if (ok) {
|
||||
assertAwaitDetection(`if (ok) {
|
||||
while (d) {
|
||||
await 5
|
||||
}
|
||||
|
@ -1237,7 +1254,7 @@ const emit = defineEmits(['a', 'b'])
|
|||
})
|
||||
|
||||
test('multiple `if for` nested statements', () => {
|
||||
assertAwaitDetection(`if (ok) {
|
||||
assertAwaitDetection(`if (ok) {
|
||||
for (let a of [1,2,3]) {
|
||||
await a
|
||||
}
|
||||
|
@ -1646,7 +1663,7 @@ describe('SFC analyze <script> bindings', () => {
|
|||
}
|
||||
)
|
||||
expect(content).toMatch(`export default {
|
||||
name: 'FooBar'`)
|
||||
__name: 'FooBar'`)
|
||||
assertCode(content)
|
||||
})
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { compileStyle } from '../src'
|
||||
import { compileStyle, parse } from '../src'
|
||||
import { mockId, compileSFCScript, assertCode } from './utils'
|
||||
|
||||
describe('CSS vars injection', () => {
|
||||
|
@ -231,5 +231,21 @@ describe('CSS vars injection', () => {
|
|||
})`)
|
||||
assertCode(content)
|
||||
})
|
||||
|
||||
// #6022
|
||||
test('should be able to parse incomplete expressions', () => {
|
||||
const {
|
||||
descriptor: { cssVars }
|
||||
} = parse(
|
||||
`<script setup>let xxx = 1</script>
|
||||
<style scoped>
|
||||
label {
|
||||
font-weight: v-bind("count.toString(");
|
||||
font-weight: v-bind(xxx);
|
||||
}
|
||||
</style>`
|
||||
)
|
||||
expect(cssVars).toMatchObject([`count.toString(`, `xxx`])
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
@ -25,6 +25,17 @@ describe('compiler sfc: rewriteDefault', () => {
|
|||
export { a as b, a as c}
|
||||
const script = a"
|
||||
`)
|
||||
|
||||
expect(
|
||||
rewriteDefault(
|
||||
`const a = 1 \n export { a as b, a as default , a as c}`,
|
||||
'script'
|
||||
)
|
||||
).toMatchInlineSnapshot(`
|
||||
"const a = 1
|
||||
export { a as b, a as c}
|
||||
const script = a"
|
||||
`)
|
||||
})
|
||||
|
||||
test('w/ comments', async () => {
|
||||
|
@ -63,6 +74,81 @@ describe('compiler sfc: rewriteDefault', () => {
|
|||
// export { myFunction as default }
|
||||
const script = a"
|
||||
`)
|
||||
|
||||
expect(
|
||||
rewriteDefault(
|
||||
`const a = 1 \n export {\n a as b,\n a as default ,\n a as c}\n` +
|
||||
`// export { myFunction as default }`,
|
||||
'script'
|
||||
)
|
||||
).toMatchInlineSnapshot(`
|
||||
"const a = 1
|
||||
export {
|
||||
a as b,
|
||||
|
||||
a as c}
|
||||
// export { myFunction as default }
|
||||
const script = a"
|
||||
`)
|
||||
})
|
||||
|
||||
test(`export { default } from '...'`, async () => {
|
||||
expect(
|
||||
rewriteDefault(`export { default, foo } from './index.js'`, 'script')
|
||||
).toMatchInlineSnapshot(`
|
||||
"import { default as __VUE_DEFAULT__ } from './index.js'
|
||||
export { foo } from './index.js'
|
||||
const script = __VUE_DEFAULT__"
|
||||
`)
|
||||
|
||||
expect(
|
||||
rewriteDefault(`export { default , foo } from './index.js'`, 'script')
|
||||
).toMatchInlineSnapshot(`
|
||||
"import { default as __VUE_DEFAULT__ } from './index.js'
|
||||
export { foo } from './index.js'
|
||||
const script = __VUE_DEFAULT__"
|
||||
`)
|
||||
|
||||
expect(
|
||||
rewriteDefault(`export { foo, default } from './index.js'`, 'script')
|
||||
).toMatchInlineSnapshot(`
|
||||
"import { default as __VUE_DEFAULT__ } from './index.js'
|
||||
export { foo, } from './index.js'
|
||||
const script = __VUE_DEFAULT__"
|
||||
`)
|
||||
|
||||
expect(
|
||||
rewriteDefault(
|
||||
`export { foo as default, bar } from './index.js'`,
|
||||
'script'
|
||||
)
|
||||
).toMatchInlineSnapshot(`
|
||||
"import { foo } from './index.js'
|
||||
export { bar } from './index.js'
|
||||
const script = foo"
|
||||
`)
|
||||
|
||||
expect(
|
||||
rewriteDefault(
|
||||
`export { foo as default , bar } from './index.js'`,
|
||||
'script'
|
||||
)
|
||||
).toMatchInlineSnapshot(`
|
||||
"import { foo } from './index.js'
|
||||
export { bar } from './index.js'
|
||||
const script = foo"
|
||||
`)
|
||||
|
||||
expect(
|
||||
rewriteDefault(
|
||||
`export { bar, foo as default } from './index.js'`,
|
||||
'script'
|
||||
)
|
||||
).toMatchInlineSnapshot(`
|
||||
"import { foo } from './index.js'
|
||||
export { bar, } from './index.js'
|
||||
const script = foo"
|
||||
`)
|
||||
})
|
||||
|
||||
test('export default class', async () => {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@vue/compiler-sfc",
|
||||
"version": "3.2.33",
|
||||
"version": "3.2.37",
|
||||
"description": "@vue/compiler-sfc",
|
||||
"main": "dist/compiler-sfc.cjs.js",
|
||||
"module": "dist/compiler-sfc.esm-browser.js",
|
||||
|
@ -33,11 +33,11 @@
|
|||
"homepage": "https://github.com/vuejs/core/tree/main/packages/compiler-sfc#readme",
|
||||
"dependencies": {
|
||||
"@babel/parser": "^7.16.4",
|
||||
"@vue/compiler-core": "3.2.33",
|
||||
"@vue/compiler-dom": "3.2.33",
|
||||
"@vue/compiler-ssr": "3.2.33",
|
||||
"@vue/reactivity-transform": "3.2.33",
|
||||
"@vue/shared": "3.2.33",
|
||||
"@vue/compiler-core": "3.2.37",
|
||||
"@vue/compiler-dom": "3.2.37",
|
||||
"@vue/compiler-ssr": "3.2.37",
|
||||
"@vue/reactivity-transform": "3.2.37",
|
||||
"@vue/shared": "3.2.37",
|
||||
"estree-walker": "^2.0.2",
|
||||
"magic-string": "^0.25.7",
|
||||
"source-map": "^0.6.1",
|
||||
|
|
|
@ -74,7 +74,7 @@ const WITH_DEFAULTS = 'withDefaults'
|
|||
const DEFAULT_VAR = `__default__`
|
||||
|
||||
const isBuiltInDir = makeMap(
|
||||
`once,memo,if,else,else-if,slot,text,html,on,bind,model,show,cloak,is`
|
||||
`once,memo,if,for,else,else-if,slot,text,html,on,bind,model,show,cloak,is`
|
||||
)
|
||||
|
||||
export interface SFCScriptCompileOptions {
|
||||
|
@ -201,7 +201,12 @@ export function compileScript(
|
|||
)
|
||||
}
|
||||
if (options.babelParserPlugins) plugins.push(...options.babelParserPlugins)
|
||||
if (isTS) plugins.push('typescript', 'decorators-legacy')
|
||||
if (isTS) {
|
||||
plugins.push('typescript')
|
||||
if (!plugins.includes('decorators')) {
|
||||
plugins.push('decorators-legacy')
|
||||
}
|
||||
}
|
||||
|
||||
if (!scriptSetup) {
|
||||
if (!script) {
|
||||
|
@ -1332,7 +1337,11 @@ export function compileScript(
|
|||
}
|
||||
|
||||
// 8. inject `useCssVars` calls
|
||||
if (cssVars.length) {
|
||||
if (
|
||||
cssVars.length &&
|
||||
// no need to do this when targeting SSR
|
||||
!(options.inlineTemplate && options.templateOptions?.ssr)
|
||||
) {
|
||||
helperImports.add(CSS_VARS_HELPER)
|
||||
helperImports.add('unref')
|
||||
s.prependRight(
|
||||
|
@ -1486,7 +1495,7 @@ export function compileScript(
|
|||
if (!hasDefaultExportName && filename && filename !== DEFAULT_FILENAME) {
|
||||
const match = filename.match(/([^/\\]+)\.\w+$/)
|
||||
if (match) {
|
||||
runtimeOptions += `\n name: '${match[1]}',`
|
||||
runtimeOptions += `\n __name: '${match[1]}',`
|
||||
}
|
||||
}
|
||||
if (hasInlinedSsrRenderFn) {
|
||||
|
@ -1833,6 +1842,7 @@ function inferRuntimeType(
|
|||
case 'WeakSet':
|
||||
case 'WeakMap':
|
||||
case 'Date':
|
||||
case 'Promise':
|
||||
return [node.typeName.name]
|
||||
case 'Record':
|
||||
case 'Partial':
|
||||
|
@ -2125,7 +2135,8 @@ function resolveTemplateUsageCheckString(sfc: SFCDescriptor) {
|
|||
}
|
||||
if (prop.exp) {
|
||||
code += `,${processExp(
|
||||
(prop.exp as SimpleExpressionNode).content
|
||||
(prop.exp as SimpleExpressionNode).content,
|
||||
prop.name
|
||||
)}`
|
||||
}
|
||||
}
|
||||
|
@ -2144,8 +2155,21 @@ function resolveTemplateUsageCheckString(sfc: SFCDescriptor) {
|
|||
return code
|
||||
}
|
||||
|
||||
function processExp(exp: string) {
|
||||
if (/ as \w|<.*>/.test(exp)) {
|
||||
const forAliasRE = /([\s\S]*?)\s+(?:in|of)\s+([\s\S]*)/
|
||||
|
||||
function processExp(exp: string, dir?: string): string {
|
||||
if (/ as\s+\w|<.*>|:/.test(exp)) {
|
||||
if (dir === 'slot') {
|
||||
exp = `(${exp})=>{}`
|
||||
} else if (dir === 'on') {
|
||||
exp = `()=>{${exp}}`
|
||||
} else if (dir === 'for') {
|
||||
const inMatch = exp.match(forAliasRE)
|
||||
if (inMatch) {
|
||||
const [, LHS, RHS] = inMatch
|
||||
return processExp(`(${LHS})=>{}`) + processExp(RHS)
|
||||
}
|
||||
}
|
||||
let ret = ''
|
||||
// has potential type cast or generic arguments that uses types
|
||||
const ast = parseExpression(exp, { plugins: ['typescript'] })
|
||||
|
|
|
@ -202,7 +202,7 @@ function doCompileTemplate({
|
|||
cacheHandlers: true,
|
||||
ssrCssVars:
|
||||
ssr && ssrCssVars && ssrCssVars.length
|
||||
? genCssVarsFromList(ssrCssVars, shortId, isProd)
|
||||
? genCssVarsFromList(ssrCssVars, shortId, isProd, true)
|
||||
: '',
|
||||
scopeId: scoped ? longId : undefined,
|
||||
slotted,
|
||||
|
|
|
@ -12,16 +12,17 @@ import { PluginCreator } from 'postcss'
|
|||
import hash from 'hash-sum'
|
||||
|
||||
export const CSS_VARS_HELPER = `useCssVars`
|
||||
// match v-bind() with max 2-levels of nested parens.
|
||||
const cssVarRE = /v-bind\s*\(((?:[^)(]+|\((?:[^)(]+|\([^)(]*\))*\))*)\)/g
|
||||
|
||||
export function genCssVarsFromList(
|
||||
vars: string[],
|
||||
id: string,
|
||||
isProd: boolean
|
||||
isProd: boolean,
|
||||
isSSR = false
|
||||
): string {
|
||||
return `{\n ${vars
|
||||
.map(key => `"${genVarName(id, key, isProd)}": (${key})`)
|
||||
.map(
|
||||
key => `"${isSSR ? `--` : ``}${genVarName(id, key, isProd)}": (${key})`
|
||||
)
|
||||
.join(',\n ')}\n}`
|
||||
}
|
||||
|
||||
|
@ -44,22 +45,71 @@ function normalizeExpression(exp: string) {
|
|||
return exp
|
||||
}
|
||||
|
||||
const vBindRE = /v-bind\s*\(/g
|
||||
|
||||
export function parseCssVars(sfc: SFCDescriptor): string[] {
|
||||
const vars: string[] = []
|
||||
sfc.styles.forEach(style => {
|
||||
let match
|
||||
// ignore v-bind() in comments /* ... */
|
||||
const content = style.content.replace(/\/\*([\s\S]*?)\*\//g, '')
|
||||
while ((match = cssVarRE.exec(content))) {
|
||||
const variable = normalizeExpression(match[1])
|
||||
if (!vars.includes(variable)) {
|
||||
vars.push(variable)
|
||||
while ((match = vBindRE.exec(content))) {
|
||||
const start = match.index + match[0].length
|
||||
const end = lexBinding(content, start)
|
||||
if (end !== null) {
|
||||
const variable = normalizeExpression(content.slice(start, end))
|
||||
if (!vars.includes(variable)) {
|
||||
vars.push(variable)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
return vars
|
||||
}
|
||||
|
||||
const enum LexerState {
|
||||
inParens,
|
||||
inSingleQuoteString,
|
||||
inDoubleQuoteString
|
||||
}
|
||||
|
||||
function lexBinding(content: string, start: number): number | null {
|
||||
let state: LexerState = LexerState.inParens
|
||||
let parenDepth = 0
|
||||
|
||||
for (let i = start; i < content.length; i++) {
|
||||
const char = content.charAt(i)
|
||||
switch (state) {
|
||||
case LexerState.inParens:
|
||||
if (char === `'`) {
|
||||
state = LexerState.inSingleQuoteString
|
||||
} else if (char === `"`) {
|
||||
state = LexerState.inDoubleQuoteString
|
||||
} else if (char === `(`) {
|
||||
parenDepth++
|
||||
} else if (char === `)`) {
|
||||
if (parenDepth > 0) {
|
||||
parenDepth--
|
||||
} else {
|
||||
return i
|
||||
}
|
||||
}
|
||||
break
|
||||
case LexerState.inSingleQuoteString:
|
||||
if (char === `'`) {
|
||||
state = LexerState.inParens
|
||||
}
|
||||
break
|
||||
case LexerState.inDoubleQuoteString:
|
||||
if (char === `"`) {
|
||||
state = LexerState.inParens
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
// for compileStyle
|
||||
export interface CssVarsPluginOptions {
|
||||
id: string
|
||||
|
@ -72,10 +122,24 @@ export const cssVarsPlugin: PluginCreator<CssVarsPluginOptions> = opts => {
|
|||
postcssPlugin: 'vue-sfc-vars',
|
||||
Declaration(decl) {
|
||||
// rewrite CSS variables
|
||||
if (cssVarRE.test(decl.value)) {
|
||||
decl.value = decl.value.replace(cssVarRE, (_, $1) => {
|
||||
return `var(--${genVarName(id, normalizeExpression($1), isProd)})`
|
||||
})
|
||||
const value = decl.value
|
||||
if (vBindRE.test(value)) {
|
||||
vBindRE.lastIndex = 0
|
||||
let transformed = ''
|
||||
let lastIndex = 0
|
||||
let match
|
||||
while ((match = vBindRE.exec(value))) {
|
||||
const start = match.index + match[0].length
|
||||
const end = lexBinding(value, start)
|
||||
if (end !== null) {
|
||||
const variable = normalizeExpression(value.slice(start, end))
|
||||
transformed +=
|
||||
value.slice(lastIndex, match.index) +
|
||||
`var(--${genVarName(id, variable, isProd)})`
|
||||
lastIndex = end + 1
|
||||
}
|
||||
}
|
||||
decl.value = transformed + value.slice(lastIndex)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ import { parse, ParserPlugin } from '@babel/parser'
|
|||
import MagicString from 'magic-string'
|
||||
|
||||
const defaultExportRE = /((?:^|\n|;)\s*)export(\s*)default/
|
||||
const namedDefaultExportRE = /((?:^|\n|;)\s*)export(.+)as(\s*)default/s
|
||||
const namedDefaultExportRE = /((?:^|\n|;)\s*)export(.+)(?:as)?(\s*)default/s
|
||||
const exportDefaultClassRE =
|
||||
/((?:^|\n|;)\s*)export\s+default\s+class\s+([\w$]+)/
|
||||
|
||||
|
@ -45,21 +45,39 @@ export function rewriteDefault(
|
|||
s.overwrite(node.start!, node.declaration.start!, `const ${as} = `)
|
||||
}
|
||||
if (node.type === 'ExportNamedDeclaration') {
|
||||
node.specifiers.forEach(specifier => {
|
||||
for (const specifier of node.specifiers) {
|
||||
if (
|
||||
specifier.type === 'ExportSpecifier' &&
|
||||
specifier.exported.type === 'Identifier' &&
|
||||
specifier.exported.name === 'default'
|
||||
) {
|
||||
const end = specifier.end!
|
||||
s.overwrite(
|
||||
specifier.start!,
|
||||
input.charAt(end) === ',' ? end + 1 : end,
|
||||
``
|
||||
)
|
||||
if (node.source) {
|
||||
if (specifier.local.name === 'default') {
|
||||
const end = specifierEnd(input, specifier.local.end!, node.end)
|
||||
s.prepend(
|
||||
`import { default as __VUE_DEFAULT__ } from '${node.source.value}'\n`
|
||||
)
|
||||
s.overwrite(specifier.start!, end, ``)
|
||||
s.append(`\nconst ${as} = __VUE_DEFAULT__`)
|
||||
continue
|
||||
} else {
|
||||
const end = specifierEnd(input, specifier.exported.end!, node.end)
|
||||
s.prepend(
|
||||
`import { ${input.slice(
|
||||
specifier.local.start!,
|
||||
specifier.local.end!
|
||||
)} } from '${node.source.value}'\n`
|
||||
)
|
||||
s.overwrite(specifier.start!, end, ``)
|
||||
s.append(`\nconst ${as} = ${specifier.local.name}`)
|
||||
continue
|
||||
}
|
||||
}
|
||||
const end = specifierEnd(input, specifier.end!, node.end)
|
||||
s.overwrite(specifier.start!, end, ``)
|
||||
s.append(`\nconst ${as} = ${specifier.local.name}`)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
return s.toString()
|
||||
|
@ -68,3 +86,21 @@ export function rewriteDefault(
|
|||
export function hasDefaultExport(input: string): boolean {
|
||||
return defaultExportRE.test(input) || namedDefaultExportRE.test(input)
|
||||
}
|
||||
|
||||
function specifierEnd(input: string, end: number, nodeEnd: number | null) {
|
||||
// export { default , foo } ...
|
||||
let hasCommas = false
|
||||
let oldEnd = end
|
||||
while (end < nodeEnd!) {
|
||||
if (/\s/.test(input.charAt(end))) {
|
||||
end++
|
||||
} else if (input.charAt(end) === ',') {
|
||||
end++
|
||||
hasCommas = true
|
||||
break
|
||||
} else if (input.charAt(end) === '}') {
|
||||
break
|
||||
}
|
||||
}
|
||||
return hasCommas ? end : oldEnd
|
||||
}
|
||||
|
|
|
@ -17,6 +17,20 @@ describe('ssr: components', () => {
|
|||
`)
|
||||
})
|
||||
|
||||
// event listeners should still be passed
|
||||
test('event listeners', () => {
|
||||
expect(compile(`<foo @click="bar" />`).code).toMatchInlineSnapshot(`
|
||||
"const { resolveComponent: _resolveComponent, mergeProps: _mergeProps } = require(\\"vue\\")
|
||||
const { ssrRenderComponent: _ssrRenderComponent } = require(\\"vue/server-renderer\\")
|
||||
|
||||
return function ssrRender(_ctx, _push, _parent, _attrs) {
|
||||
const _component_foo = _resolveComponent(\\"foo\\")
|
||||
|
||||
_push(_ssrRenderComponent(_component_foo, _mergeProps({ onClick: _ctx.bar }, _attrs), null, _parent))
|
||||
}"
|
||||
`)
|
||||
})
|
||||
|
||||
test('dynamic component', () => {
|
||||
expect(compile(`<component is="foo" prop="b" />`).code)
|
||||
.toMatchInlineSnapshot(`
|
||||
|
@ -266,112 +280,68 @@ describe('ssr: components', () => {
|
|||
`)
|
||||
})
|
||||
|
||||
test('built-in fallthroughs', () => {
|
||||
expect(compile(`<transition><div/></transition>`).code)
|
||||
.toMatchInlineSnapshot(`
|
||||
"const { ssrRenderAttrs: _ssrRenderAttrs } = require(\\"vue/server-renderer\\")
|
||||
describe('built-in fallthroughs', () => {
|
||||
test('transition', () => {
|
||||
expect(compile(`<transition><div/></transition>`).code)
|
||||
.toMatchInlineSnapshot(`
|
||||
"const { ssrRenderAttrs: _ssrRenderAttrs } = require(\\"vue/server-renderer\\")
|
||||
|
||||
return function ssrRender(_ctx, _push, _parent, _attrs) {
|
||||
_push(\`<div\${_ssrRenderAttrs(_attrs)}></div>\`)
|
||||
}"
|
||||
`)
|
||||
|
||||
expect(compile(`<keep-alive><foo/></keep-alive>`).code)
|
||||
.toMatchInlineSnapshot(`
|
||||
"const { resolveComponent: _resolveComponent } = require(\\"vue\\")
|
||||
const { ssrRenderComponent: _ssrRenderComponent } = require(\\"vue/server-renderer\\")
|
||||
|
||||
return function ssrRender(_ctx, _push, _parent, _attrs) {
|
||||
const _component_foo = _resolveComponent(\\"foo\\")
|
||||
|
||||
_push(_ssrRenderComponent(_component_foo, _attrs, null, _parent))
|
||||
}"
|
||||
`)
|
||||
})
|
||||
|
||||
// transition-group should flatten and concat its children fragments into
|
||||
// a single one
|
||||
describe('transition-group', () => {
|
||||
test('basic', () => {
|
||||
expect(
|
||||
compile(
|
||||
`<transition-group><div v-for="i in list"/></transition-group>`
|
||||
).code
|
||||
).toMatchInlineSnapshot(`
|
||||
"const { ssrRenderList: _ssrRenderList } = require(\\"vue/server-renderer\\")
|
||||
|
||||
return function ssrRender(_ctx, _push, _parent, _attrs) {
|
||||
_push(\`<!--[-->\`)
|
||||
_ssrRenderList(_ctx.list, (i) => {
|
||||
_push(\`<div></div>\`)
|
||||
})
|
||||
_push(\`<!--]-->\`)
|
||||
}"
|
||||
`)
|
||||
return function ssrRender(_ctx, _push, _parent, _attrs) {
|
||||
_push(\`<div\${_ssrRenderAttrs(_attrs)}></div>\`)
|
||||
}"
|
||||
`)
|
||||
})
|
||||
|
||||
test('with static tag', () => {
|
||||
expect(
|
||||
compile(
|
||||
`<transition-group tag="ul"><div v-for="i in list"/></transition-group>`
|
||||
).code
|
||||
).toMatchInlineSnapshot(`
|
||||
"const { ssrRenderList: _ssrRenderList } = require(\\"vue/server-renderer\\")
|
||||
test('keep-alive', () => {
|
||||
expect(compile(`<keep-alive><foo/></keep-alive>`).code)
|
||||
.toMatchInlineSnapshot(`
|
||||
"const { resolveComponent: _resolveComponent } = require(\\"vue\\")
|
||||
const { ssrRenderComponent: _ssrRenderComponent } = require(\\"vue/server-renderer\\")
|
||||
|
||||
return function ssrRender(_ctx, _push, _parent, _attrs) {
|
||||
_push(\`<ul>\`)
|
||||
_ssrRenderList(_ctx.list, (i) => {
|
||||
_push(\`<div></div>\`)
|
||||
})
|
||||
_push(\`</ul>\`)
|
||||
}"
|
||||
`)
|
||||
return function ssrRender(_ctx, _push, _parent, _attrs) {
|
||||
const _component_foo = _resolveComponent(\\"foo\\")
|
||||
|
||||
_push(_ssrRenderComponent(_component_foo, _attrs, null, _parent))
|
||||
}"
|
||||
`)
|
||||
})
|
||||
|
||||
test('with dynamic tag', () => {
|
||||
// #5352
|
||||
test('should push marker string if is slot root', () => {
|
||||
expect(
|
||||
compile(
|
||||
`<transition-group :tag="someTag"><div v-for="i in list"/></transition-group>`
|
||||
).code
|
||||
compile(`<foo><transition><div v-if="false"/></transition></foo>`)
|
||||
.code
|
||||
).toMatchInlineSnapshot(`
|
||||
"const { ssrRenderList: _ssrRenderList } = require(\\"vue/server-renderer\\")
|
||||
"const { resolveComponent: _resolveComponent, withCtx: _withCtx, openBlock: _openBlock, createBlock: _createBlock, createCommentVNode: _createCommentVNode, Transition: _Transition, createVNode: _createVNode } = require(\\"vue\\")
|
||||
const { ssrRenderComponent: _ssrRenderComponent } = require(\\"vue/server-renderer\\")
|
||||
|
||||
return function ssrRender(_ctx, _push, _parent, _attrs) {
|
||||
_push(\`<\${_ctx.someTag}>\`)
|
||||
_ssrRenderList(_ctx.list, (i) => {
|
||||
_push(\`<div></div>\`)
|
||||
})
|
||||
_push(\`</\${_ctx.someTag}>\`)
|
||||
}"
|
||||
`)
|
||||
})
|
||||
const _component_foo = _resolveComponent(\\"foo\\")
|
||||
|
||||
test('with multi fragments children', () => {
|
||||
expect(
|
||||
compile(
|
||||
`<transition-group>
|
||||
<div v-for="i in 10"/>
|
||||
<div v-for="i in 10"/>
|
||||
<template v-if="ok"><div>ok</div></template>
|
||||
</transition-group>`
|
||||
).code
|
||||
).toMatchInlineSnapshot(`
|
||||
"const { ssrRenderList: _ssrRenderList } = require(\\"vue/server-renderer\\")
|
||||
|
||||
return function ssrRender(_ctx, _push, _parent, _attrs) {
|
||||
_push(\`<!--[-->\`)
|
||||
_ssrRenderList(10, (i) => {
|
||||
_push(\`<div></div>\`)
|
||||
})
|
||||
_ssrRenderList(10, (i) => {
|
||||
_push(\`<div></div>\`)
|
||||
})
|
||||
if (_ctx.ok) {
|
||||
_push(\`<div>ok</div>\`)
|
||||
} else {
|
||||
_push(\`<!---->\`)
|
||||
}
|
||||
_push(\`<!--]-->\`)
|
||||
_push(_ssrRenderComponent(_component_foo, _attrs, {
|
||||
default: _withCtx((_, _push, _parent, _scopeId) => {
|
||||
if (_push) {
|
||||
_push(\`\`)
|
||||
if (false) {
|
||||
_push(\`<div\${_scopeId}></div>\`)
|
||||
} else {
|
||||
_push(\`<!---->\`)
|
||||
}
|
||||
} else {
|
||||
return [
|
||||
_createVNode(_Transition, null, {
|
||||
default: _withCtx(() => [
|
||||
false
|
||||
? (_openBlock(), _createBlock(\\"div\\", { key: 0 }))
|
||||
: _createCommentVNode(\\"v-if\\", true)
|
||||
]),
|
||||
_: 1 /* STABLE */
|
||||
})
|
||||
]
|
||||
}
|
||||
}),
|
||||
_: 1 /* STABLE */
|
||||
}, _parent))
|
||||
}"
|
||||
`)
|
||||
})
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
import { compile } from '../src'
|
||||
|
||||
describe('ssr: attrs fallthrough', () => {
|
||||
test('basic', () => {
|
||||
expect(compile(`<div/>`).code).toMatchInlineSnapshot(`
|
||||
"const { ssrRenderAttrs: _ssrRenderAttrs } = require(\\"vue/server-renderer\\")
|
||||
|
||||
return function ssrRender(_ctx, _push, _parent, _attrs) {
|
||||
_push(\`<div\${_ssrRenderAttrs(_attrs)}></div>\`)
|
||||
}"
|
||||
`)
|
||||
})
|
||||
|
||||
test('with comments', () => {
|
||||
expect(compile(`<!--!--><div/>`).code).toMatchInlineSnapshot(`
|
||||
"const { ssrRenderAttrs: _ssrRenderAttrs } = require(\\"vue/server-renderer\\")
|
||||
|
||||
return function ssrRender(_ctx, _push, _parent, _attrs) {
|
||||
_push(\`<!--[--><!--!--><div\${_ssrRenderAttrs(_attrs)}></div><!--]-->\`)
|
||||
}"
|
||||
`)
|
||||
})
|
||||
|
||||
// #5140
|
||||
test('should not inject to non-single-root if branches', () => {
|
||||
expect(compile(`<div v-if="true"/><div/>`).code).toMatchInlineSnapshot(`
|
||||
"
|
||||
return function ssrRender(_ctx, _push, _parent, _attrs) {
|
||||
_push(\`<!--[-->\`)
|
||||
if (true) {
|
||||
_push(\`<div></div>\`)
|
||||
} else {
|
||||
_push(\`<!---->\`)
|
||||
}
|
||||
_push(\`<div></div><!--]-->\`)
|
||||
}"
|
||||
`)
|
||||
})
|
||||
|
||||
test('fallthrough component content (root with coomments)', () => {
|
||||
expect(compile(`<!--root--><transition><div/></transition>`).code)
|
||||
.toMatchInlineSnapshot(`
|
||||
"const { ssrRenderAttrs: _ssrRenderAttrs } = require(\\"vue/server-renderer\\")
|
||||
|
||||
return function ssrRender(_ctx, _push, _parent, _attrs) {
|
||||
_push(\`<!--[--><!--root--><div\${_ssrRenderAttrs(_attrs)}></div><!--]-->\`)
|
||||
}"
|
||||
`)
|
||||
})
|
||||
|
||||
test('should not inject to fallthrough component content if not root', () => {
|
||||
expect(compile(`<div/><transition><div/></transition>`).code)
|
||||
.toMatchInlineSnapshot(`
|
||||
"
|
||||
return function ssrRender(_ctx, _push, _parent, _attrs) {
|
||||
_push(\`<!--[--><div></div><div></div><!--]-->\`)
|
||||
}"
|
||||
`)
|
||||
})
|
||||
})
|
|
@ -123,7 +123,7 @@ describe('ssr: <slot>', () => {
|
|||
"const { ssrRenderSlotInner: _ssrRenderSlotInner } = require(\\"vue/server-renderer\\")
|
||||
|
||||
return function ssrRender(_ctx, _push, _parent, _attrs) {
|
||||
_ssrRenderSlotInner(_ctx.$slots, \\"default\\", {}, null, _push, _parent)
|
||||
_ssrRenderSlotInner(_ctx.$slots, \\"default\\", {}, null, _push, _parent, null, true)
|
||||
}"
|
||||
`)
|
||||
})
|
||||
|
|
|
@ -0,0 +1,111 @@
|
|||
import { compile } from '../src'
|
||||
|
||||
// transition-group should flatten and concat its children fragments into
|
||||
// a single one
|
||||
describe('transition-group', () => {
|
||||
test('basic', () => {
|
||||
expect(
|
||||
compile(`<transition-group><div v-for="i in list"/></transition-group>`)
|
||||
.code
|
||||
).toMatchInlineSnapshot(`
|
||||
"const { ssrRenderList: _ssrRenderList } = require(\\"vue/server-renderer\\")
|
||||
|
||||
return function ssrRender(_ctx, _push, _parent, _attrs) {
|
||||
_push(\`<!--[-->\`)
|
||||
_ssrRenderList(_ctx.list, (i) => {
|
||||
_push(\`<div></div>\`)
|
||||
})
|
||||
_push(\`<!--]-->\`)
|
||||
}"
|
||||
`)
|
||||
})
|
||||
|
||||
test('with static tag', () => {
|
||||
expect(
|
||||
compile(
|
||||
`<transition-group tag="ul"><div v-for="i in list"/></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(
|
||||
`<transition-group :tag="someTag"><div v-for="i in list"/></transition-group>`
|
||||
).code
|
||||
).toMatchInlineSnapshot(`
|
||||
"const { ssrRenderAttrs: _ssrRenderAttrs, ssrRenderList: _ssrRenderList } = require(\\"vue/server-renderer\\")
|
||||
|
||||
return function ssrRender(_ctx, _push, _parent, _attrs) {
|
||||
_push(\`<\${
|
||||
_ctx.someTag
|
||||
}\${
|
||||
_ssrRenderAttrs(_attrs)
|
||||
}>\`)
|
||||
_ssrRenderList(_ctx.list, (i) => {
|
||||
_push(\`<div></div>\`)
|
||||
})
|
||||
_push(\`</\${_ctx.someTag}>\`)
|
||||
}"
|
||||
`)
|
||||
})
|
||||
|
||||
test('with multi fragments children', () => {
|
||||
expect(
|
||||
compile(
|
||||
`<transition-group>
|
||||
<div v-for="i in 10"/>
|
||||
<div v-for="i in 10"/>
|
||||
<template v-if="ok"><div>ok</div></template>
|
||||
</transition-group>`
|
||||
).code
|
||||
).toMatchInlineSnapshot(`
|
||||
"const { ssrRenderList: _ssrRenderList } = require(\\"vue/server-renderer\\")
|
||||
|
||||
return function ssrRender(_ctx, _push, _parent, _attrs) {
|
||||
_push(\`<!--[-->\`)
|
||||
_ssrRenderList(10, (i) => {
|
||||
_push(\`<div></div>\`)
|
||||
})
|
||||
_ssrRenderList(10, (i) => {
|
||||
_push(\`<div></div>\`)
|
||||
})
|
||||
if (_ctx.ok) {
|
||||
_push(\`<div>ok</div>\`)
|
||||
} else {
|
||||
_push(\`<!---->\`)
|
||||
}
|
||||
_push(\`<!--]-->\`)
|
||||
}"
|
||||
`)
|
||||
})
|
||||
|
||||
test('attribute fallthrough', () => {
|
||||
expect(
|
||||
compile(
|
||||
`<transition-group tag="ul" class="red" id="ok">
|
||||
</transition-group>`
|
||||
).code
|
||||
).toMatchInlineSnapshot(`
|
||||
"const { mergeProps: _mergeProps } = require(\\"vue\\")
|
||||
const { ssrRenderAttrs: _ssrRenderAttrs } = require(\\"vue/server-renderer\\")
|
||||
|
||||
return function ssrRender(_ctx, _push, _parent, _attrs) {
|
||||
_push(\`<ul\${_ssrRenderAttrs(_mergeProps({
|
||||
class: \\"red\\",
|
||||
id: \\"ok\\"
|
||||
}, _attrs))}></ul>\`)
|
||||
}"
|
||||
`)
|
||||
})
|
||||
})
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@vue/compiler-ssr",
|
||||
"version": "3.2.33",
|
||||
"version": "3.2.37",
|
||||
"description": "@vue/compiler-ssr",
|
||||
"main": "dist/compiler-ssr.cjs.js",
|
||||
"types": "dist/compiler-ssr.d.ts",
|
||||
|
@ -28,7 +28,7 @@
|
|||
},
|
||||
"homepage": "https://github.com/vuejs/core/tree/main/packages/compiler-ssr#readme",
|
||||
"dependencies": {
|
||||
"@vue/shared": "3.2.33",
|
||||
"@vue/compiler-dom": "3.2.33"
|
||||
"@vue/shared": "3.2.37",
|
||||
"@vue/compiler-dom": "3.2.37"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,7 +10,8 @@ import {
|
|||
trackSlotScopes,
|
||||
noopDirectiveTransform,
|
||||
transformBind,
|
||||
transformStyle
|
||||
transformStyle,
|
||||
transformOn
|
||||
} from '@vue/compiler-dom'
|
||||
import { ssrCodegenTransform } from './ssrCodegenTransform'
|
||||
import { ssrTransformElement } from './transforms/ssrTransformElement'
|
||||
|
@ -70,11 +71,12 @@ export function compile(
|
|||
directiveTransforms: {
|
||||
// reusing core v-bind
|
||||
bind: transformBind,
|
||||
on: transformOn,
|
||||
// model and show has dedicated SSR handling
|
||||
model: ssrTransformModel,
|
||||
show: ssrTransformShow,
|
||||
// the following are ignored during SSR
|
||||
on: noopDirectiveTransform,
|
||||
// on: noopDirectiveTransform,
|
||||
cloak: noopDirectiveTransform,
|
||||
once: noopDirectiveTransform,
|
||||
memo: noopDirectiveTransform,
|
||||
|
|
|
@ -51,7 +51,7 @@ export function ssrCodegenTransform(ast: RootNode, options: CompilerOptions) {
|
|||
|
||||
const isFragment =
|
||||
ast.children.length > 1 && ast.children.some(c => !isText(c))
|
||||
processChildren(ast.children, context, isFragment)
|
||||
processChildren(ast, context, isFragment)
|
||||
ast.codegenNode = createBlockStatement(context.body)
|
||||
|
||||
// Finalize helpers.
|
||||
|
@ -125,8 +125,12 @@ function createChildContext(
|
|||
)
|
||||
}
|
||||
|
||||
interface Container {
|
||||
children: TemplateChildNode[]
|
||||
}
|
||||
|
||||
export function processChildren(
|
||||
children: TemplateChildNode[],
|
||||
parent: Container,
|
||||
context: SSRTransformContext,
|
||||
asFragment = false,
|
||||
disableNestedFragments = false
|
||||
|
@ -134,6 +138,7 @@ export function processChildren(
|
|||
if (asFragment) {
|
||||
context.pushStringPart(`<!--[-->`)
|
||||
}
|
||||
const { children } = parent
|
||||
for (let i = 0; i < children.length; i++) {
|
||||
const child = children[i]
|
||||
switch (child.type) {
|
||||
|
@ -143,7 +148,7 @@ export function processChildren(
|
|||
ssrProcessElement(child, context)
|
||||
break
|
||||
case ElementTypes.COMPONENT:
|
||||
ssrProcessComponent(child, context)
|
||||
ssrProcessComponent(child, context, parent)
|
||||
break
|
||||
case ElementTypes.SLOT:
|
||||
ssrProcessSlotOutlet(child, context)
|
||||
|
@ -208,12 +213,12 @@ export function processChildren(
|
|||
}
|
||||
|
||||
export function processChildrenAsStatement(
|
||||
children: TemplateChildNode[],
|
||||
parent: Container,
|
||||
parentContext: SSRTransformContext,
|
||||
asFragment = false,
|
||||
withSlotScopeId = parentContext.withSlotScopeId
|
||||
): BlockStatement {
|
||||
const childContext = createChildContext(parentContext, withSlotScopeId)
|
||||
processChildren(children, childContext, asFragment)
|
||||
processChildren(parent, childContext, asFragment)
|
||||
return createBlockStatement(childContext.body)
|
||||
}
|
||||
|
|
|
@ -11,8 +11,11 @@ import {
|
|||
isBuiltInType
|
||||
} from '@vue/compiler-dom'
|
||||
|
||||
const filterChild = (node: ParentNode) =>
|
||||
node.children.filter(n => n.type !== NodeTypes.COMMENT)
|
||||
|
||||
const hasSingleChild = (node: ParentNode): boolean =>
|
||||
node.children.filter(n => n.type !== NodeTypes.COMMENT).length === 1
|
||||
filterChild(node).length === 1
|
||||
|
||||
export const ssrInjectFallthroughAttrs: NodeTransform = (node, context) => {
|
||||
// _attrs is provided as a function argument.
|
||||
|
@ -28,10 +31,13 @@ export const ssrInjectFallthroughAttrs: NodeTransform = (node, context) => {
|
|||
(isBuiltInType(node.tag, 'Transition') ||
|
||||
isBuiltInType(node.tag, 'KeepAlive'))
|
||||
) {
|
||||
if (hasSingleChild(node)) {
|
||||
injectFallthroughAttrs(node.children[0])
|
||||
const rootChildren = filterChild(context.root)
|
||||
if (rootChildren.length === 1 && rootChildren[0] === node) {
|
||||
if (hasSingleChild(node)) {
|
||||
injectFallthroughAttrs(node.children[0])
|
||||
}
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
const parent = context.parent
|
||||
|
@ -40,6 +46,25 @@ export const ssrInjectFallthroughAttrs: NodeTransform = (node, context) => {
|
|||
}
|
||||
|
||||
if (node.type === NodeTypes.IF_BRANCH && hasSingleChild(node)) {
|
||||
// detect cases where the parent v-if is not the only root level node
|
||||
let hasEncounteredIf = false
|
||||
for (const c of filterChild(parent)) {
|
||||
if (
|
||||
c.type === NodeTypes.IF ||
|
||||
(c.type === NodeTypes.ELEMENT && findDir(c, 'if'))
|
||||
) {
|
||||
// multiple root v-if
|
||||
if (hasEncounteredIf) return
|
||||
hasEncounteredIf = true
|
||||
} else if (
|
||||
// node before v-if
|
||||
!hasEncounteredIf ||
|
||||
// non else nodes
|
||||
!(c.type === NodeTypes.ELEMENT && findDir(c, /else/, true))
|
||||
) {
|
||||
return
|
||||
}
|
||||
}
|
||||
injectFallthroughAttrs(node.children[0])
|
||||
} else if (hasSingleChild(parent)) {
|
||||
injectFallthroughAttrs(node)
|
||||
|
|
|
@ -34,7 +34,9 @@ import {
|
|||
TRANSITION_GROUP,
|
||||
CREATE_VNODE,
|
||||
CallExpression,
|
||||
JSChildNode
|
||||
JSChildNode,
|
||||
RESOLVE_DYNAMIC_COMPONENT,
|
||||
TRANSITION
|
||||
} from '@vue/compiler-dom'
|
||||
import { SSR_RENDER_COMPONENT, SSR_RENDER_VNODE } from '../runtimeHelpers'
|
||||
import {
|
||||
|
@ -47,7 +49,10 @@ import {
|
|||
ssrProcessSuspense,
|
||||
ssrTransformSuspense
|
||||
} from './ssrTransformSuspense'
|
||||
import { ssrProcessTransitionGroup } from './ssrTransformTransitionGroup'
|
||||
import {
|
||||
ssrProcessTransitionGroup,
|
||||
ssrTransformTransitionGroup
|
||||
} from './ssrTransformTransitionGroup'
|
||||
import { isSymbol, isObject, isArray } from '@vue/shared'
|
||||
import { buildSSRProps } from './ssrTransformElement'
|
||||
|
||||
|
@ -57,7 +62,10 @@ import { buildSSRProps } from './ssrTransformElement'
|
|||
// pass and complete them in the 2nd pass.
|
||||
const wipMap = new WeakMap<ComponentNode, WIPSlotEntry[]>()
|
||||
|
||||
const WIP_SLOT = Symbol()
|
||||
|
||||
interface WIPSlotEntry {
|
||||
type: typeof WIP_SLOT
|
||||
fn: FunctionExpression
|
||||
children: TemplateChildNode[]
|
||||
vnodeBranch: ReturnStatement
|
||||
|
@ -83,13 +91,18 @@ export const ssrTransformComponent: NodeTransform = (node, context) => {
|
|||
}
|
||||
|
||||
const component = resolveComponentType(node, context, true /* ssr */)
|
||||
const isDynamicComponent =
|
||||
isObject(component) && component.callee === RESOLVE_DYNAMIC_COMPONENT
|
||||
componentTypeMap.set(node, component)
|
||||
|
||||
if (isSymbol(component)) {
|
||||
if (component === SUSPENSE) {
|
||||
return ssrTransformSuspense(node, context)
|
||||
}
|
||||
return // built-in component: fallthrough
|
||||
if (component === TRANSITION_GROUP) {
|
||||
return ssrTransformTransitionGroup(node, context)
|
||||
}
|
||||
return // other built-in components: fallthrough
|
||||
}
|
||||
|
||||
// Build the fallback vnode-based branch for the component's slots.
|
||||
|
@ -116,7 +129,13 @@ export const ssrTransformComponent: NodeTransform = (node, context) => {
|
|||
if (node.props.length) {
|
||||
// note we are not passing ssr: true here because for components, v-on
|
||||
// handlers should still be passed
|
||||
const { props, directives } = buildProps(node, context)
|
||||
const { props, directives } = buildProps(
|
||||
node,
|
||||
context,
|
||||
undefined,
|
||||
true,
|
||||
isDynamicComponent
|
||||
)
|
||||
if (props || directives.length) {
|
||||
propsExp = buildSSRProps(props, directives, context)
|
||||
}
|
||||
|
@ -134,6 +153,7 @@ export const ssrTransformComponent: NodeTransform = (node, context) => {
|
|||
loc
|
||||
)
|
||||
wipEntries.push({
|
||||
type: WIP_SLOT,
|
||||
fn,
|
||||
children,
|
||||
// also collect the corresponding vnode branch built earlier
|
||||
|
@ -173,7 +193,8 @@ export const ssrTransformComponent: NodeTransform = (node, context) => {
|
|||
|
||||
export function ssrProcessComponent(
|
||||
node: ComponentNode,
|
||||
context: SSRTransformContext
|
||||
context: SSRTransformContext,
|
||||
parent: { children: TemplateChildNode[] }
|
||||
) {
|
||||
const component = componentTypeMap.get(node)!
|
||||
if (!node.ssrCodegenNode) {
|
||||
|
@ -187,13 +208,23 @@ export function ssrProcessComponent(
|
|||
} else {
|
||||
// real fall-through: Transition / KeepAlive
|
||||
// just render its children.
|
||||
processChildren(node.children, context)
|
||||
// #5352: if is at root level of a slot, push an empty string.
|
||||
// this does not affect the final output, but avoids all-comment slot
|
||||
// content of being treated as empty by ssrRenderSlot().
|
||||
if ((parent as WIPSlotEntry).type === WIP_SLOT) {
|
||||
context.pushStringPart(``)
|
||||
}
|
||||
// #5351: filter out comment children inside transition
|
||||
if (component === TRANSITION) {
|
||||
node.children = node.children.filter(c => c.type !== NodeTypes.COMMENT)
|
||||
}
|
||||
processChildren(node, context)
|
||||
}
|
||||
} else {
|
||||
// finish up slot function expressions from the 1st pass.
|
||||
const wipEntries = wipMap.get(node) || []
|
||||
for (let i = 0; i < wipEntries.length; i++) {
|
||||
const { fn, children, vnodeBranch } = wipEntries[i]
|
||||
const { fn, vnodeBranch } = wipEntries[i]
|
||||
// For each slot, we generate two branches: one SSR-optimized branch and
|
||||
// one normal vnode-based branch. The branches are taken based on the
|
||||
// presence of the 2nd `_push` argument (which is only present if the slot
|
||||
|
@ -201,7 +232,7 @@ export function ssrProcessComponent(
|
|||
fn.body = createIfStatement(
|
||||
createSimpleExpression(`_push`, false),
|
||||
processChildrenAsStatement(
|
||||
children,
|
||||
wipEntries[i],
|
||||
context,
|
||||
false,
|
||||
true /* withSlotScopeId */
|
||||
|
|
|
@ -89,6 +89,8 @@ export const ssrTransformElement: NodeTransform = (node, context) => {
|
|||
node,
|
||||
context,
|
||||
node.props,
|
||||
false /* isComponent */,
|
||||
false /* isDynamicComponent */,
|
||||
true /* ssr */
|
||||
)
|
||||
if (props || directives.length) {
|
||||
|
@ -194,7 +196,7 @@ export const ssrTransformElement: NodeTransform = (node, context) => {
|
|||
if (!needMergeProps) {
|
||||
node.children = [createInterpolation(prop.exp, prop.loc)]
|
||||
}
|
||||
} else if (!needMergeProps) {
|
||||
} else if (!needMergeProps && prop.name !== 'on') {
|
||||
// Directive transforms.
|
||||
const directiveTransform = context.directiveTransforms[prop.name]
|
||||
if (directiveTransform) {
|
||||
|
@ -426,7 +428,7 @@ export function ssrProcessElement(
|
|||
if (rawChildren) {
|
||||
context.pushStringPart(rawChildren)
|
||||
} else if (node.children.length) {
|
||||
processChildren(node.children, context)
|
||||
processChildren(node, context)
|
||||
}
|
||||
|
||||
if (!isVoidTag(node.tag)) {
|
||||
|
|
|
@ -50,6 +50,10 @@ export const ssrTransformSlotOutlet: NodeTransform = (node, context) => {
|
|||
parent.children.filter(c => c.type === NodeTypes.ELEMENT).length === 1
|
||||
) {
|
||||
method = SSR_RENDER_SLOT_INNER
|
||||
if (!(context.scopeId && context.slotted !== false)) {
|
||||
args.push('null')
|
||||
}
|
||||
args.push('true')
|
||||
}
|
||||
|
||||
node.ssrCodegenNode = createCallExpression(context.helper(method), args)
|
||||
|
@ -65,7 +69,7 @@ export function ssrProcessSlotOutlet(
|
|||
// has fallback content
|
||||
if (node.children.length) {
|
||||
const fallbackRenderFn = createFunctionExpression([])
|
||||
fallbackRenderFn.body = processChildrenAsStatement(node.children, context)
|
||||
fallbackRenderFn.body = processChildrenAsStatement(node, context)
|
||||
// _renderSlot(slots, name, props, fallback, ...)
|
||||
renderCall.arguments[3] = fallbackRenderFn
|
||||
}
|
||||
|
|
|
@ -66,8 +66,8 @@ export function ssrProcessSuspense(
|
|||
}
|
||||
const { slotsExp, wipSlots } = wipEntry
|
||||
for (let i = 0; i < wipSlots.length; i++) {
|
||||
const { fn, children } = wipSlots[i]
|
||||
fn.body = processChildrenAsStatement(children, context)
|
||||
const slot = wipSlots[i]
|
||||
slot.fn.body = processChildrenAsStatement(slot, context)
|
||||
}
|
||||
// _push(ssrRenderSuspense(slots))
|
||||
context.pushStatement(
|
||||
|
|
|
@ -58,7 +58,7 @@ export function ssrProcessTeleport(
|
|||
false, // isSlot
|
||||
node.loc
|
||||
)
|
||||
contentRenderFn.body = processChildrenAsStatement(node.children, context)
|
||||
contentRenderFn.body = processChildrenAsStatement(node, context)
|
||||
context.pushStatement(
|
||||
createCallExpression(context.helper(SSR_RENDER_TELEPORT), [
|
||||
`_push`,
|
||||
|
|
|
@ -1,20 +1,75 @@
|
|||
import { ComponentNode, findProp, NodeTypes } from '@vue/compiler-dom'
|
||||
import {
|
||||
AttributeNode,
|
||||
buildProps,
|
||||
ComponentNode,
|
||||
createCallExpression,
|
||||
DirectiveNode,
|
||||
findProp,
|
||||
JSChildNode,
|
||||
NodeTypes,
|
||||
TransformContext
|
||||
} from '@vue/compiler-dom'
|
||||
import { SSR_RENDER_ATTRS } from '../runtimeHelpers'
|
||||
import { processChildren, SSRTransformContext } from '../ssrCodegenTransform'
|
||||
import { buildSSRProps } from './ssrTransformElement'
|
||||
|
||||
const wipMap = new WeakMap<ComponentNode, WIPEntry>()
|
||||
|
||||
interface WIPEntry {
|
||||
tag: AttributeNode | DirectiveNode
|
||||
propsExp: string | JSChildNode | null
|
||||
}
|
||||
|
||||
// phase 1: build props
|
||||
export function ssrTransformTransitionGroup(
|
||||
node: ComponentNode,
|
||||
context: TransformContext
|
||||
) {
|
||||
return () => {
|
||||
const tag = findProp(node, 'tag')
|
||||
if (tag) {
|
||||
const otherProps = node.props.filter(p => p !== tag)
|
||||
const { props, directives } = buildProps(
|
||||
node,
|
||||
context,
|
||||
otherProps,
|
||||
true, /* isComponent */
|
||||
false, /* isDynamicComponent */
|
||||
true /* ssr (skip event listeners) */
|
||||
)
|
||||
let propsExp = null
|
||||
if (props || directives.length) {
|
||||
propsExp = createCallExpression(context.helper(SSR_RENDER_ATTRS), [
|
||||
buildSSRProps(props, directives, context)
|
||||
])
|
||||
}
|
||||
wipMap.set(node, {
|
||||
tag,
|
||||
propsExp
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// phase 2: process children
|
||||
export function ssrProcessTransitionGroup(
|
||||
node: ComponentNode,
|
||||
context: SSRTransformContext
|
||||
) {
|
||||
const tag = findProp(node, 'tag')
|
||||
if (tag) {
|
||||
const entry = wipMap.get(node)
|
||||
if (entry) {
|
||||
const { tag, propsExp } = entry
|
||||
if (tag.type === NodeTypes.DIRECTIVE) {
|
||||
// dynamic :tag
|
||||
context.pushStringPart(`<`)
|
||||
context.pushStringPart(tag.exp!)
|
||||
if (propsExp) {
|
||||
context.pushStringPart(propsExp)
|
||||
}
|
||||
context.pushStringPart(`>`)
|
||||
|
||||
processChildren(
|
||||
node.children,
|
||||
node,
|
||||
context,
|
||||
false,
|
||||
/**
|
||||
|
@ -30,12 +85,16 @@ export function ssrProcessTransitionGroup(
|
|||
context.pushStringPart(`>`)
|
||||
} else {
|
||||
// static tag
|
||||
context.pushStringPart(`<${tag.value!.content}>`)
|
||||
processChildren(node.children, context, false, true)
|
||||
context.pushStringPart(`<${tag.value!.content}`)
|
||||
if (propsExp) {
|
||||
context.pushStringPart(propsExp)
|
||||
}
|
||||
context.pushStringPart(`>`)
|
||||
processChildren(node, context, false, true)
|
||||
context.pushStringPart(`</${tag.value!.content}>`)
|
||||
}
|
||||
} else {
|
||||
// fragment
|
||||
processChildren(node.children, context, true, true)
|
||||
processChildren(node, context, true, true)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,7 +33,7 @@ export function ssrProcessFor(
|
|||
createForLoopParams(node.parseResult)
|
||||
)
|
||||
renderLoop.body = processChildrenAsStatement(
|
||||
node.children,
|
||||
node,
|
||||
context,
|
||||
needFragmentWrapper
|
||||
)
|
||||
|
|
|
@ -72,5 +72,5 @@ function processIfBranch(
|
|||
(children.length !== 1 || children[0].type !== NodeTypes.ELEMENT) &&
|
||||
// optimize away nested fragments when the only child is a ForNode
|
||||
!(children.length === 1 && children[0].type === NodeTypes.FOR)
|
||||
return processChildrenAsStatement(children, context, needFragmentWrapper)
|
||||
return processChildrenAsStatement(branch, context, needFragmentWrapper)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2018-present, Yuxi (Evan) You
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@vue/reactivity-transform",
|
||||
"version": "3.2.33",
|
||||
"version": "3.2.37",
|
||||
"description": "@vue/reactivity-transform",
|
||||
"main": "dist/reactivity-transform.cjs.js",
|
||||
"files": [
|
||||
|
@ -29,8 +29,8 @@
|
|||
"homepage": "https://github.com/vuejs/core/tree/dev/packages/reactivity-transform#readme",
|
||||
"dependencies": {
|
||||
"@babel/parser": "^7.16.4",
|
||||
"@vue/compiler-core": "3.2.33",
|
||||
"@vue/shared": "3.2.33",
|
||||
"@vue/compiler-core": "3.2.37",
|
||||
"@vue/shared": "3.2.37",
|
||||
"estree-walker": "^2.0.2",
|
||||
"magic-string": "^0.25.7"
|
||||
},
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@vue/reactivity",
|
||||
"version": "3.2.33",
|
||||
"version": "3.2.37",
|
||||
"description": "@vue/reactivity",
|
||||
"main": "index.js",
|
||||
"module": "dist/reactivity.esm-bundler.js",
|
||||
|
@ -36,6 +36,6 @@
|
|||
},
|
||||
"homepage": "https://github.com/vuejs/core/tree/main/packages/reactivity#readme",
|
||||
"dependencies": {
|
||||
"@vue/shared": "3.2.33"
|
||||
"@vue/shared": "3.2.37"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -231,6 +231,7 @@ describe('api: lifecycle hooks', () => {
|
|||
}
|
||||
|
||||
const Mid = {
|
||||
props: ['count'],
|
||||
setup(props: any) {
|
||||
onBeforeMount(() => calls.push('mid onBeforeMount'))
|
||||
onMounted(() => calls.push('mid onMounted'))
|
||||
|
@ -243,6 +244,7 @@ describe('api: lifecycle hooks', () => {
|
|||
}
|
||||
|
||||
const Child = {
|
||||
props: ['count'],
|
||||
setup(props: any) {
|
||||
onBeforeMount(() => calls.push('child onBeforeMount'))
|
||||
onMounted(() => calls.push('child onMounted'))
|
||||
|
|
|
@ -75,39 +75,6 @@ describe('api: setup context', () => {
|
|||
expect(dummy).toBe(1)
|
||||
})
|
||||
|
||||
it('setup props should resolve the correct types from props object', async () => {
|
||||
const count = ref(0)
|
||||
let dummy
|
||||
|
||||
const Parent = {
|
||||
render: () => h(Child, { count: count.value })
|
||||
}
|
||||
|
||||
const Child = defineComponent({
|
||||
props: {
|
||||
count: Number
|
||||
},
|
||||
|
||||
setup(props) {
|
||||
watchEffect(() => {
|
||||
dummy = props.count
|
||||
})
|
||||
return () => h('div', props.count)
|
||||
}
|
||||
})
|
||||
|
||||
const root = nodeOps.createElement('div')
|
||||
render(h(Parent), root)
|
||||
expect(serializeInner(root)).toMatch(`<div>0</div>`)
|
||||
expect(dummy).toBe(0)
|
||||
|
||||
// props should be reactive
|
||||
count.value++
|
||||
await nextTick()
|
||||
expect(serializeInner(root)).toMatch(`<div>1</div>`)
|
||||
expect(dummy).toBe(1)
|
||||
})
|
||||
|
||||
it('context.attrs', async () => {
|
||||
const toggle = ref(true)
|
||||
|
||||
|
|
|
@ -13,7 +13,8 @@ import {
|
|||
createTextVNode,
|
||||
createVNode,
|
||||
withDirectives,
|
||||
vModelCheckbox
|
||||
vModelCheckbox,
|
||||
renderSlot
|
||||
} from '@vue/runtime-dom'
|
||||
import { renderToString, SSRContext } from '@vue/server-renderer'
|
||||
import { PatchFlags } from '../../shared/src'
|
||||
|
@ -96,6 +97,28 @@ describe('SSR hydration', () => {
|
|||
expect(s.children).toBe(staticContent)
|
||||
})
|
||||
|
||||
// #6008
|
||||
test('static (with text node as starting node)', () => {
|
||||
const html = ` A <span>foo</span> B`
|
||||
const { vnode, container } = mountWithHydration(html, () =>
|
||||
createStaticVNode(` A <span>foo</span> B`, 3)
|
||||
)
|
||||
expect(vnode.el).toBe(container.firstChild)
|
||||
expect(vnode.anchor).toBe(container.lastChild)
|
||||
expect(`Hydration node mismatch`).not.toHaveBeenWarned()
|
||||
})
|
||||
|
||||
test('static with content adoption', () => {
|
||||
const html = ` A <span>foo</span> B`
|
||||
const { vnode, container } = mountWithHydration(html, () =>
|
||||
createStaticVNode(``, 3)
|
||||
)
|
||||
expect(vnode.el).toBe(container.firstChild)
|
||||
expect(vnode.anchor).toBe(container.lastChild)
|
||||
expect(vnode.children).toBe(html)
|
||||
expect(`Hydration node mismatch`).not.toHaveBeenWarned()
|
||||
})
|
||||
|
||||
test('element with text children', async () => {
|
||||
const msg = ref('foo')
|
||||
const { vnode, container } = mountWithHydration(
|
||||
|
@ -202,7 +225,7 @@ describe('SSR hydration', () => {
|
|||
const fn = jest.fn()
|
||||
const teleportContainer = document.createElement('div')
|
||||
teleportContainer.id = 'teleport'
|
||||
teleportContainer.innerHTML = `<span>foo</span><span class="foo"></span><!---->`
|
||||
teleportContainer.innerHTML = `<span>foo</span><span class="foo"></span><!--teleport anchor-->`
|
||||
document.body.appendChild(teleportContainer)
|
||||
|
||||
const { vnode, container } = mountWithHydration(
|
||||
|
@ -233,7 +256,7 @@ describe('SSR hydration', () => {
|
|||
msg.value = 'bar'
|
||||
await nextTick()
|
||||
expect(teleportContainer.innerHTML).toBe(
|
||||
`<span>bar</span><span class="bar"></span><!---->`
|
||||
`<span>bar</span><span class="bar"></span><!--teleport anchor-->`
|
||||
)
|
||||
})
|
||||
|
||||
|
@ -263,7 +286,7 @@ describe('SSR hydration', () => {
|
|||
|
||||
const teleportHtml = ctx.teleports!['#teleport2']
|
||||
expect(teleportHtml).toMatchInlineSnapshot(
|
||||
`"<span>foo</span><span class=\\"foo\\"></span><!----><span>foo2</span><span class=\\"foo2\\"></span><!---->"`
|
||||
`"<span>foo</span><span class=\\"foo\\"></span><!--teleport anchor--><span>foo2</span><span class=\\"foo2\\"></span><!--teleport anchor-->"`
|
||||
)
|
||||
|
||||
teleportContainer.innerHTML = teleportHtml
|
||||
|
@ -300,7 +323,7 @@ describe('SSR hydration', () => {
|
|||
msg.value = 'bar'
|
||||
await nextTick()
|
||||
expect(teleportContainer.innerHTML).toMatchInlineSnapshot(
|
||||
`"<span>bar</span><span class=\\"bar\\"></span><!----><span>bar2</span><span class=\\"bar2\\"></span><!---->"`
|
||||
`"<span>bar</span><span class=\\"bar\\"></span><!--teleport anchor--><span>bar2</span><span class=\\"bar2\\"></span><!--teleport anchor-->"`
|
||||
)
|
||||
})
|
||||
|
||||
|
@ -327,7 +350,7 @@ describe('SSR hydration', () => {
|
|||
)
|
||||
|
||||
const teleportHtml = ctx.teleports!['#teleport3']
|
||||
expect(teleportHtml).toMatchInlineSnapshot(`"<!---->"`)
|
||||
expect(teleportHtml).toMatchInlineSnapshot(`"<!--teleport anchor-->"`)
|
||||
|
||||
teleportContainer.innerHTML = teleportHtml
|
||||
document.body.appendChild(teleportContainer)
|
||||
|
@ -366,6 +389,67 @@ describe('SSR hydration', () => {
|
|||
)
|
||||
})
|
||||
|
||||
test('Teleport (as component root)', () => {
|
||||
const teleportContainer = document.createElement('div')
|
||||
teleportContainer.id = 'teleport4'
|
||||
teleportContainer.innerHTML = `hello<!--teleport anchor-->`
|
||||
document.body.appendChild(teleportContainer)
|
||||
|
||||
const wrapper = {
|
||||
render() {
|
||||
return h(Teleport, { to: '#teleport4' }, ['hello'])
|
||||
}
|
||||
}
|
||||
|
||||
const { vnode, container } = mountWithHydration(
|
||||
'<div><!--teleport start--><!--teleport end--><div></div></div>',
|
||||
() => h('div', [h(wrapper), h('div')])
|
||||
)
|
||||
expect(vnode.el).toBe(container.firstChild)
|
||||
// component el
|
||||
const wrapperVNode = (vnode as any).children[0]
|
||||
const tpStart = container.firstChild?.firstChild
|
||||
const tpEnd = tpStart?.nextSibling
|
||||
expect(wrapperVNode.el).toBe(tpStart)
|
||||
expect(wrapperVNode.component.subTree.el).toBe(tpStart)
|
||||
expect(wrapperVNode.component.subTree.anchor).toBe(tpEnd)
|
||||
// next node hydrate properly
|
||||
const nextVNode = (vnode as any).children[1]
|
||||
expect(nextVNode.el).toBe(container.firstChild?.lastChild)
|
||||
})
|
||||
|
||||
test('Teleport (nested)', () => {
|
||||
const teleportContainer = document.createElement('div')
|
||||
teleportContainer.id = 'teleport5'
|
||||
teleportContainer.innerHTML = `<div><!--teleport start--><!--teleport end--></div><!--teleport anchor--><div>child</div><!--teleport anchor-->`
|
||||
document.body.appendChild(teleportContainer)
|
||||
|
||||
const { vnode, container } = mountWithHydration(
|
||||
'<!--teleport start--><!--teleport end-->',
|
||||
() =>
|
||||
h(Teleport, { to: '#teleport5' }, [
|
||||
h('div', [h(Teleport, { to: '#teleport5' }, [h('div', 'child')])])
|
||||
])
|
||||
)
|
||||
|
||||
expect(vnode.el).toBe(container.firstChild)
|
||||
expect(vnode.anchor).toBe(container.lastChild)
|
||||
|
||||
const childDivVNode = (vnode as any).children[0]
|
||||
const div = teleportContainer.firstChild
|
||||
expect(childDivVNode.el).toBe(div)
|
||||
expect(vnode.targetAnchor).toBe(div?.nextSibling)
|
||||
|
||||
const childTeleportVNode = childDivVNode.children[0]
|
||||
expect(childTeleportVNode.el).toBe(div?.firstChild)
|
||||
expect(childTeleportVNode.anchor).toBe(div?.lastChild)
|
||||
|
||||
expect(childTeleportVNode.targetAnchor).toBe(teleportContainer.lastChild)
|
||||
expect(childTeleportVNode.children[0].el).toBe(
|
||||
teleportContainer.lastChild?.previousSibling
|
||||
)
|
||||
})
|
||||
|
||||
// compile SSR + client render fn from the same template & hydrate
|
||||
test('full compiler integration', async () => {
|
||||
const mounted: string[] = []
|
||||
|
@ -851,6 +935,53 @@ describe('SSR hydration', () => {
|
|||
expect((container.firstChild!.firstChild as any)._value).toBe(true)
|
||||
})
|
||||
|
||||
// #5728
|
||||
test('empty text node in slot', () => {
|
||||
const Comp = {
|
||||
render(this: any) {
|
||||
return renderSlot(this.$slots, 'default', {}, () => [
|
||||
createTextVNode('')
|
||||
])
|
||||
}
|
||||
}
|
||||
const { container, vnode } = mountWithHydration('<!--[--><!--]-->', () =>
|
||||
h(Comp)
|
||||
)
|
||||
expect(container.childNodes.length).toBe(3)
|
||||
const text = container.childNodes[1]
|
||||
expect(text.nodeType).toBe(3)
|
||||
expect(vnode.el).toBe(container.childNodes[0])
|
||||
// component => slot fragment => text node
|
||||
expect((vnode as any).component?.subTree.children[0].el).toBe(text)
|
||||
})
|
||||
|
||||
test('app.unmount()', async () => {
|
||||
const container = document.createElement('DIV')
|
||||
container.innerHTML = '<button></button>'
|
||||
const App = defineComponent({
|
||||
setup(_, { expose }) {
|
||||
const count = ref(0)
|
||||
|
||||
expose({ count })
|
||||
|
||||
return () =>
|
||||
h('button', {
|
||||
onClick: () => count.value++
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
const app = createSSRApp(App)
|
||||
const vm = app.mount(container)
|
||||
await nextTick()
|
||||
expect((container as any)._vnode).toBeDefined()
|
||||
// @ts-expect-error - expose()'d properties are not available on vm type
|
||||
expect(vm.count).toBe(0)
|
||||
|
||||
app.unmount()
|
||||
expect((container as any)._vnode).toBe(null)
|
||||
})
|
||||
|
||||
describe('mismatch handling', () => {
|
||||
test('text node', () => {
|
||||
const { container } = mountWithHydration(`foo`, () => 'bar')
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@vue/runtime-core",
|
||||
"version": "3.2.33",
|
||||
"version": "3.2.37",
|
||||
"description": "@vue/runtime-core",
|
||||
"main": "index.js",
|
||||
"module": "dist/runtime-core.esm-bundler.js",
|
||||
|
@ -32,7 +32,7 @@
|
|||
},
|
||||
"homepage": "https://github.com/vuejs/core/tree/main/packages/runtime-core#readme",
|
||||
"dependencies": {
|
||||
"@vue/shared": "3.2.33",
|
||||
"@vue/reactivity": "3.2.33"
|
||||
"@vue/shared": "3.2.37",
|
||||
"@vue/reactivity": "3.2.37"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -111,7 +111,7 @@ export function defineAsyncComponent<
|
|||
)
|
||||
}
|
||||
|
||||
return defineComponent<{}>({
|
||||
return defineComponent({
|
||||
name: 'AsyncComponentWrapper',
|
||||
|
||||
__asyncLoader: load,
|
||||
|
|
|
@ -6,8 +6,7 @@ import {
|
|||
ComponentOptionsWithObjectProps,
|
||||
ComponentOptionsMixin,
|
||||
RenderFunction,
|
||||
ComponentOptionsBase,
|
||||
ComponentProvideOptions
|
||||
ComponentOptionsBase
|
||||
} from './componentOptions'
|
||||
import {
|
||||
SetupContext,
|
||||
|
@ -41,8 +40,6 @@ export type DefineComponent<
|
|||
Extends extends ComponentOptionsMixin = ComponentOptionsMixin,
|
||||
E extends EmitsOptions = {},
|
||||
EE extends string = string,
|
||||
Provide extends ComponentProvideOptions = ComponentProvideOptions,
|
||||
RawOptions extends {} = {},
|
||||
PP = PublicProps,
|
||||
Props = Readonly<
|
||||
PropsOrPropOptions extends ComponentPropsOptions
|
||||
|
@ -51,23 +48,22 @@ export type DefineComponent<
|
|||
> &
|
||||
({} extends E ? {} : EmitsToProps<E>),
|
||||
Defaults = ExtractDefaultPropTypes<PropsOrPropOptions>
|
||||
> = RawOptions &
|
||||
ComponentPublicInstanceConstructor<
|
||||
CreateComponentPublicInstance<
|
||||
Props,
|
||||
RawBindings,
|
||||
D,
|
||||
C,
|
||||
M,
|
||||
Mixin,
|
||||
Extends,
|
||||
E,
|
||||
PP & Props,
|
||||
Defaults,
|
||||
true
|
||||
> &
|
||||
Props
|
||||
> = ComponentPublicInstanceConstructor<
|
||||
CreateComponentPublicInstance<
|
||||
Props,
|
||||
RawBindings,
|
||||
D,
|
||||
C,
|
||||
M,
|
||||
Mixin,
|
||||
Extends,
|
||||
E,
|
||||
PP & Props,
|
||||
Defaults,
|
||||
true
|
||||
> &
|
||||
Props
|
||||
> &
|
||||
ComponentOptionsBase<
|
||||
Props,
|
||||
RawBindings,
|
||||
|
@ -78,8 +74,7 @@ export type DefineComponent<
|
|||
Extends,
|
||||
E,
|
||||
EE,
|
||||
Defaults,
|
||||
Provide
|
||||
Defaults
|
||||
> &
|
||||
PP
|
||||
|
||||
|
@ -109,36 +104,20 @@ export function defineComponent<
|
|||
Mixin extends ComponentOptionsMixin = ComponentOptionsMixin,
|
||||
Extends extends ComponentOptionsMixin = ComponentOptionsMixin,
|
||||
E extends EmitsOptions = EmitsOptions,
|
||||
EE extends string = string,
|
||||
Provide extends ComponentProvideOptions = ComponentProvideOptions,
|
||||
Options extends {} = {}
|
||||
EE extends string = string
|
||||
>(
|
||||
options: Options &
|
||||
ComponentOptionsWithoutProps<
|
||||
Props,
|
||||
RawBindings,
|
||||
D,
|
||||
C,
|
||||
M,
|
||||
Mixin,
|
||||
Extends,
|
||||
E,
|
||||
EE,
|
||||
Provide
|
||||
>
|
||||
): DefineComponent<
|
||||
Props,
|
||||
RawBindings,
|
||||
D,
|
||||
C,
|
||||
M,
|
||||
Mixin,
|
||||
Extends,
|
||||
E,
|
||||
EE,
|
||||
Provide,
|
||||
Options
|
||||
>
|
||||
options: ComponentOptionsWithoutProps<
|
||||
Props,
|
||||
RawBindings,
|
||||
D,
|
||||
C,
|
||||
M,
|
||||
Mixin,
|
||||
Extends,
|
||||
E,
|
||||
EE
|
||||
>
|
||||
): DefineComponent<Props, RawBindings, D, C, M, Mixin, Extends, E, EE>
|
||||
|
||||
// overload 3: object format with array props declaration
|
||||
// props inferred as { [key in PropNames]?: any }
|
||||
|
@ -152,23 +131,19 @@ export function defineComponent<
|
|||
Mixin extends ComponentOptionsMixin = ComponentOptionsMixin,
|
||||
Extends extends ComponentOptionsMixin = ComponentOptionsMixin,
|
||||
E extends EmitsOptions = Record<string, any>,
|
||||
EE extends string = string,
|
||||
Provide extends ComponentProvideOptions = ComponentProvideOptions,
|
||||
Options extends {} = {}
|
||||
EE extends string = string
|
||||
>(
|
||||
options: Options &
|
||||
ComponentOptionsWithArrayProps<
|
||||
PropNames,
|
||||
RawBindings,
|
||||
D,
|
||||
C,
|
||||
M,
|
||||
Mixin,
|
||||
Extends,
|
||||
E,
|
||||
EE,
|
||||
Provide
|
||||
>
|
||||
options: ComponentOptionsWithArrayProps<
|
||||
PropNames,
|
||||
RawBindings,
|
||||
D,
|
||||
C,
|
||||
M,
|
||||
Mixin,
|
||||
Extends,
|
||||
E,
|
||||
EE
|
||||
>
|
||||
): DefineComponent<
|
||||
Readonly<{ [key in PropNames]?: any }>,
|
||||
RawBindings,
|
||||
|
@ -178,9 +153,7 @@ export function defineComponent<
|
|||
Mixin,
|
||||
Extends,
|
||||
E,
|
||||
EE,
|
||||
Provide,
|
||||
Options
|
||||
EE
|
||||
>
|
||||
|
||||
// overload 4: object format with object props declaration
|
||||
|
@ -196,36 +169,20 @@ export function defineComponent<
|
|||
Mixin extends ComponentOptionsMixin = ComponentOptionsMixin,
|
||||
Extends extends ComponentOptionsMixin = ComponentOptionsMixin,
|
||||
E extends EmitsOptions = Record<string, any>,
|
||||
EE extends string = string,
|
||||
Provide extends ComponentProvideOptions = ComponentProvideOptions,
|
||||
Options extends {} = {}
|
||||
EE extends string = string
|
||||
>(
|
||||
options: Options &
|
||||
ComponentOptionsWithObjectProps<
|
||||
PropsOptions,
|
||||
RawBindings,
|
||||
D,
|
||||
C,
|
||||
M,
|
||||
Mixin,
|
||||
Extends,
|
||||
E,
|
||||
EE,
|
||||
Provide
|
||||
>
|
||||
): DefineComponent<
|
||||
PropsOptions,
|
||||
RawBindings,
|
||||
D,
|
||||
C,
|
||||
M,
|
||||
Mixin,
|
||||
Extends,
|
||||
E,
|
||||
EE,
|
||||
Provide,
|
||||
Options
|
||||
>
|
||||
options: ComponentOptionsWithObjectProps<
|
||||
PropsOptions,
|
||||
RawBindings,
|
||||
D,
|
||||
C,
|
||||
M,
|
||||
Mixin,
|
||||
Extends,
|
||||
E,
|
||||
EE
|
||||
>
|
||||
): DefineComponent<PropsOptions, RawBindings, D, C, M, Mixin, Extends, E, EE>
|
||||
|
||||
// implementation, close to no-op
|
||||
export function defineComponent(options: unknown) {
|
||||
|
|
|
@ -139,7 +139,7 @@ type InferDefault<P, T> = T extends
|
|||
| boolean
|
||||
| symbol
|
||||
| Function
|
||||
? T
|
||||
? T | ((props: P) => T)
|
||||
: (props: P) => T
|
||||
|
||||
type PropsWithDefaults<Base, Defaults> = Base & {
|
||||
|
|
|
@ -381,9 +381,10 @@ function installLegacyAPIs(app: App) {
|
|||
|
||||
function applySingletonAppMutations(app: App) {
|
||||
// copy over asset registries and deopt flag
|
||||
;['mixins', 'components', 'directives', 'filters', 'deopt'].forEach(key => {
|
||||
app._context.mixins = [...singletonApp._context.mixins]
|
||||
;['components', 'directives', 'filters'].forEach(key => {
|
||||
// @ts-ignore
|
||||
app._context[key] = singletonApp._context[key]
|
||||
app._context[key] = Object.create(singletonApp._context[key])
|
||||
})
|
||||
|
||||
// copy over global config mutations
|
||||
|
@ -398,7 +399,7 @@ function applySingletonAppMutations(app: App) {
|
|||
}
|
||||
const val = singletonApp.config[key as keyof AppConfig]
|
||||
// @ts-ignore
|
||||
app.config[key] = val
|
||||
app.config[key] = isObject(val) ? Object.create(val) : val
|
||||
|
||||
// compat for runtime ignoredElements -> isCustomElement
|
||||
if (
|
||||
|
|
|
@ -106,6 +106,10 @@ export interface ComponentInternalOptions {
|
|||
* This one should be exposed so that devtools can make use of it
|
||||
*/
|
||||
__file?: string
|
||||
/**
|
||||
* name inferred from filename
|
||||
*/
|
||||
__name?: string
|
||||
}
|
||||
|
||||
export interface FunctionalComponent<P = {}, E extends EmitsOptions = {}>
|
||||
|
@ -949,11 +953,12 @@ const classify = (str: string): string =>
|
|||
str.replace(classifyRE, c => c.toUpperCase()).replace(/[-_]/g, '')
|
||||
|
||||
export function getComponentName(
|
||||
Component: ConcreteComponent
|
||||
): string | undefined {
|
||||
Component: ConcreteComponent,
|
||||
includeInferred = true
|
||||
): string | false | undefined {
|
||||
return isFunction(Component)
|
||||
? Component.displayName || Component.name
|
||||
: Component.name
|
||||
: Component.name || (includeInferred && Component.__name)
|
||||
}
|
||||
|
||||
/* istanbul ignore next */
|
||||
|
|
|
@ -58,7 +58,8 @@ import { EmitsOptions, EmitsToProps } from './componentEmits'
|
|||
import { Directive } from './directives'
|
||||
import {
|
||||
CreateComponentPublicInstance,
|
||||
ComponentPublicInstance
|
||||
ComponentPublicInstance,
|
||||
isReservedPrefix
|
||||
} from './componentPublicInstance'
|
||||
import { warn } from './warning'
|
||||
import { VNodeChild } from './vnode'
|
||||
|
@ -117,9 +118,8 @@ export interface ComponentOptionsBase<
|
|||
Extends extends ComponentOptionsMixin,
|
||||
E extends EmitsOptions,
|
||||
EE extends string = string,
|
||||
Defaults = {},
|
||||
Provide extends ComponentProvideOptions = ComponentProvideOptions
|
||||
> extends LegacyOptions<Props, D, C, M, Mixin, Extends, Provide>,
|
||||
Defaults = {}
|
||||
> extends LegacyOptions<Props, D, C, M, Mixin, Extends>,
|
||||
ComponentInternalOptions,
|
||||
ComponentCustomOptions {
|
||||
setup?: (
|
||||
|
@ -225,7 +225,6 @@ export type ComponentOptionsWithoutProps<
|
|||
Extends extends ComponentOptionsMixin = ComponentOptionsMixin,
|
||||
E extends EmitsOptions = EmitsOptions,
|
||||
EE extends string = string,
|
||||
Provide extends ComponentProvideOptions = ComponentProvideOptions,
|
||||
PE = Props & EmitsToProps<E>
|
||||
> = ComponentOptionsBase<
|
||||
PE,
|
||||
|
@ -237,8 +236,7 @@ export type ComponentOptionsWithoutProps<
|
|||
Extends,
|
||||
E,
|
||||
EE,
|
||||
{},
|
||||
Provide
|
||||
{}
|
||||
> & {
|
||||
props?: undefined
|
||||
} & ThisType<
|
||||
|
@ -255,7 +253,6 @@ export type ComponentOptionsWithArrayProps<
|
|||
Extends extends ComponentOptionsMixin = ComponentOptionsMixin,
|
||||
E extends EmitsOptions = EmitsOptions,
|
||||
EE extends string = string,
|
||||
Provide extends ComponentProvideOptions = ComponentProvideOptions,
|
||||
Props = Readonly<{ [key in PropNames]?: any }> & EmitsToProps<E>
|
||||
> = ComponentOptionsBase<
|
||||
Props,
|
||||
|
@ -267,8 +264,7 @@ export type ComponentOptionsWithArrayProps<
|
|||
Extends,
|
||||
E,
|
||||
EE,
|
||||
{},
|
||||
Provide
|
||||
{}
|
||||
> & {
|
||||
props: PropNames[]
|
||||
} & ThisType<
|
||||
|
@ -294,7 +290,6 @@ export type ComponentOptionsWithObjectProps<
|
|||
Extends extends ComponentOptionsMixin = ComponentOptionsMixin,
|
||||
E extends EmitsOptions = EmitsOptions,
|
||||
EE extends string = string,
|
||||
Provide extends ComponentProvideOptions = ComponentProvideOptions,
|
||||
Props = Readonly<ExtractPropTypes<PropsOptions>> & EmitsToProps<E>,
|
||||
Defaults = ExtractDefaultPropTypes<PropsOptions>
|
||||
> = ComponentOptionsBase<
|
||||
|
@ -307,8 +302,7 @@ export type ComponentOptionsWithObjectProps<
|
|||
Extends,
|
||||
E,
|
||||
EE,
|
||||
Defaults,
|
||||
Provide
|
||||
Defaults
|
||||
> & {
|
||||
props: PropsOptions & ThisType<void>
|
||||
} & ThisType<
|
||||
|
@ -408,8 +402,7 @@ interface LegacyOptions<
|
|||
C extends ComputedOptions,
|
||||
M extends MethodOptions,
|
||||
Mixin extends ComponentOptionsMixin,
|
||||
Extends extends ComponentOptionsMixin,
|
||||
Provide extends ComponentProvideOptions = ComponentProvideOptions
|
||||
Extends extends ComponentOptionsMixin
|
||||
> {
|
||||
compatConfig?: CompatConfig
|
||||
|
||||
|
@ -443,7 +436,7 @@ interface LegacyOptions<
|
|||
computed?: C
|
||||
methods?: M
|
||||
watch?: ComponentWatchOptions
|
||||
provide?: Provide
|
||||
provide?: ComponentProvideOptions
|
||||
inject?: ComponentInjectOptions
|
||||
|
||||
// assets
|
||||
|
@ -681,7 +674,7 @@ export function applyOptions(instance: ComponentInternalInstance) {
|
|||
for (const key in data) {
|
||||
checkDuplicateProperties!(OptionTypes.DATA, key)
|
||||
// expose data on ctx during dev
|
||||
if (key[0] !== '$' && key[0] !== '_') {
|
||||
if (!isReservedPrefix(key[0])) {
|
||||
Object.defineProperty(ctx, key, {
|
||||
configurable: true,
|
||||
enumerable: true,
|
||||
|
|
|
@ -34,8 +34,7 @@ import {
|
|||
OptionTypesKeys,
|
||||
resolveMergedOptions,
|
||||
shouldCacheAccess,
|
||||
MergedComponentOptionsOverride,
|
||||
ComponentProvideOptions
|
||||
MergedComponentOptionsOverride
|
||||
} from './componentOptions'
|
||||
import { EmitsOptions, EmitFn } from './componentEmits'
|
||||
import { Slots } from './componentSlots'
|
||||
|
@ -151,8 +150,7 @@ export type CreateComponentPublicInstance<
|
|||
PublicM extends MethodOptions = UnwrapMixinsType<PublicMixin, 'M'> &
|
||||
EnsureNonVoid<M>,
|
||||
PublicDefaults = UnwrapMixinsType<PublicMixin, 'Defaults'> &
|
||||
EnsureNonVoid<Defaults>,
|
||||
Provide extends ComponentProvideOptions = ComponentProvideOptions
|
||||
EnsureNonVoid<Defaults>
|
||||
> = ComponentPublicInstance<
|
||||
PublicP,
|
||||
PublicB,
|
||||
|
@ -163,19 +161,7 @@ export type CreateComponentPublicInstance<
|
|||
PublicProps,
|
||||
PublicDefaults,
|
||||
MakeDefaultsOptional,
|
||||
ComponentOptionsBase<
|
||||
P,
|
||||
B,
|
||||
D,
|
||||
C,
|
||||
M,
|
||||
Mixin,
|
||||
Extends,
|
||||
E,
|
||||
string,
|
||||
Defaults,
|
||||
Provide
|
||||
>
|
||||
ComponentOptionsBase<P, B, D, C, M, Mixin, Extends, E, string, Defaults>
|
||||
>
|
||||
|
||||
// public properties exposed on the proxy, which is used as the render context
|
||||
|
@ -274,6 +260,8 @@ export interface ComponentRenderContext {
|
|||
_: ComponentInternalInstance
|
||||
}
|
||||
|
||||
export const isReservedPrefix = (key: string) => key === '_' || key === '$'
|
||||
|
||||
export const PublicInstanceProxyHandlers: ProxyHandler<any> = {
|
||||
get({ _: instance }: ComponentRenderContext, key: string) {
|
||||
const { ctx, setupState, data, props, accessCache, type, appContext } =
|
||||
|
@ -385,11 +373,7 @@ export const PublicInstanceProxyHandlers: ProxyHandler<any> = {
|
|||
// to infinite warning loop
|
||||
key.indexOf('__v') !== 0)
|
||||
) {
|
||||
if (
|
||||
data !== EMPTY_OBJ &&
|
||||
(key[0] === '$' || key[0] === '_') &&
|
||||
hasOwn(data, key)
|
||||
) {
|
||||
if (data !== EMPTY_OBJ && isReservedPrefix(key[0]) && hasOwn(data, key)) {
|
||||
warn(
|
||||
`Property ${JSON.stringify(
|
||||
key
|
||||
|
@ -571,7 +555,7 @@ export function exposeSetupStateOnRenderContext(
|
|||
const { ctx, setupState } = instance
|
||||
Object.keys(toRaw(setupState)).forEach(key => {
|
||||
if (!setupState.__isScriptSetup) {
|
||||
if (key[0] === '$' || key[0] === '_') {
|
||||
if (isReservedPrefix(key[0])) {
|
||||
warn(
|
||||
`setup() return property ${JSON.stringify(
|
||||
key
|
||||
|
|
|
@ -38,6 +38,8 @@ export function markAttrsAccessed() {
|
|||
accessedAttrs = true
|
||||
}
|
||||
|
||||
type SetRootFn = ((root: VNode) => void) | undefined
|
||||
|
||||
export function renderComponentRoot(
|
||||
instance: ComponentInternalInstance
|
||||
): VNode {
|
||||
|
@ -121,7 +123,7 @@ export function renderComponentRoot(
|
|||
// in dev mode, comments are preserved, and it's possible for a template
|
||||
// to have comments along side the root element which makes it a fragment
|
||||
let root = result
|
||||
let setRoot: ((root: VNode) => void) | undefined = undefined
|
||||
let setRoot: SetRootFn = undefined
|
||||
if (
|
||||
__DEV__ &&
|
||||
result.patchFlag > 0 &&
|
||||
|
@ -246,9 +248,7 @@ export function renderComponentRoot(
|
|||
* template into a fragment root, but we need to locate the single element
|
||||
* root for attrs and scope id processing.
|
||||
*/
|
||||
const getChildRoot = (
|
||||
vnode: VNode
|
||||
): [VNode, ((root: VNode) => void) | undefined] => {
|
||||
const getChildRoot = (vnode: VNode): [VNode, SetRootFn] => {
|
||||
const rawChildren = vnode.children as VNodeArrayChildren
|
||||
const dynamicChildren = vnode.dynamicChildren
|
||||
const childRoot = filterSingleRoot(rawChildren)
|
||||
|
@ -257,7 +257,7 @@ const getChildRoot = (
|
|||
}
|
||||
const index = rawChildren.indexOf(childRoot)
|
||||
const dynamicIndex = dynamicChildren ? dynamicChildren.indexOf(childRoot) : -1
|
||||
const setRoot = (updatedRoot: VNode) => {
|
||||
const setRoot: SetRootFn = (updatedRoot: VNode) => {
|
||||
rawChildren[index] = updatedRoot
|
||||
if (dynamicChildren) {
|
||||
if (dynamicIndex > -1) {
|
||||
|
|
|
@ -96,8 +96,11 @@ const KeepAliveImpl: ComponentOptions = {
|
|||
|
||||
// if the internal renderer is not registered, it indicates that this is server-side rendering,
|
||||
// for KeepAlive, we just need to render its children
|
||||
if (!sharedContext.renderer) {
|
||||
return slots.default
|
||||
if (__SSR__ && !sharedContext.renderer) {
|
||||
return () => {
|
||||
const children = slots.default && slots.default()
|
||||
return children && children.length === 1 ? children[0] : children
|
||||
}
|
||||
}
|
||||
|
||||
const cache: Cache = new Map()
|
||||
|
|
|
@ -353,7 +353,26 @@ function hydrateTeleport(
|
|||
vnode.targetAnchor = targetNode
|
||||
} else {
|
||||
vnode.anchor = nextSibling(node)
|
||||
vnode.targetAnchor = hydrateChildren(
|
||||
|
||||
// lookahead until we find the target anchor
|
||||
// we cannot rely on return value of hydrateChildren() because there
|
||||
// could be nested teleports
|
||||
let targetAnchor = targetNode
|
||||
while (targetAnchor) {
|
||||
targetAnchor = nextSibling(targetAnchor)
|
||||
if (
|
||||
targetAnchor &&
|
||||
targetAnchor.nodeType === 8 &&
|
||||
(targetAnchor as Comment).data === 'teleport anchor'
|
||||
) {
|
||||
vnode.targetAnchor = targetAnchor
|
||||
;(target as TeleportTargetElement)._lpa =
|
||||
vnode.targetAnchor && nextSibling(vnode.targetAnchor as Node)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
hydrateChildren(
|
||||
targetNode,
|
||||
vnode,
|
||||
target,
|
||||
|
@ -363,8 +382,6 @@ function hydrateTeleport(
|
|||
optimized
|
||||
)
|
||||
}
|
||||
;(target as TeleportTargetElement)._lpa =
|
||||
vnode.targetAnchor && nextSibling(vnode.targetAnchor as Node)
|
||||
}
|
||||
}
|
||||
return vnode.anchor && nextSibling(vnode.anchor as Node)
|
||||
|
|
|
@ -86,7 +86,10 @@ function resolveAsset(
|
|||
|
||||
// explicit self name has highest priority
|
||||
if (type === COMPONENTS) {
|
||||
const selfName = getComponentName(Component)
|
||||
const selfName = getComponentName(
|
||||
Component,
|
||||
false /* do not include inferred name to avoid breaking existing code */
|
||||
)
|
||||
if (
|
||||
selfName &&
|
||||
(selfName === name ||
|
||||
|
|
|
@ -27,7 +27,7 @@ import { isAsyncWrapper } from './apiAsyncComponent'
|
|||
|
||||
export type RootHydrateFunction = (
|
||||
vnode: VNode<Node, Element>,
|
||||
container: Element | ShadowRoot
|
||||
container: (Element | ShadowRoot) & { _vnode?: VNode }
|
||||
) => void
|
||||
|
||||
const enum DOMNodeTypes {
|
||||
|
@ -55,7 +55,15 @@ export function createHydrationFunctions(
|
|||
const {
|
||||
mt: mountComponent,
|
||||
p: patch,
|
||||
o: { patchProp, nextSibling, parentNode, remove, insert, createComment }
|
||||
o: {
|
||||
patchProp,
|
||||
createText,
|
||||
nextSibling,
|
||||
parentNode,
|
||||
remove,
|
||||
insert,
|
||||
createComment
|
||||
}
|
||||
} = rendererInternals
|
||||
|
||||
const hydrate: RootHydrateFunction = (vnode, container) => {
|
||||
|
@ -67,11 +75,13 @@ export function createHydrationFunctions(
|
|||
)
|
||||
patch(null, vnode, container)
|
||||
flushPostFlushCbs()
|
||||
container._vnode = vnode
|
||||
return
|
||||
}
|
||||
hasMismatch = false
|
||||
hydrateNode(container.firstChild!, vnode, null, null, null)
|
||||
flushPostFlushCbs()
|
||||
container._vnode = vnode
|
||||
if (hasMismatch && !__TEST__) {
|
||||
// this error should show up in production
|
||||
console.error(`Hydration completed but contains mismatches.`)
|
||||
|
@ -110,7 +120,14 @@ export function createHydrationFunctions(
|
|||
switch (type) {
|
||||
case Text:
|
||||
if (domType !== DOMNodeTypes.TEXT) {
|
||||
nextNode = onMismatch()
|
||||
// #5728 empty text node inside a slot can cause hydration failure
|
||||
// because the server rendered HTML won't contain a text node
|
||||
if (vnode.children === '') {
|
||||
insert((vnode.el = createText('')), parentNode(node)!, node)
|
||||
nextNode = node
|
||||
} else {
|
||||
nextNode = onMismatch()
|
||||
}
|
||||
} else {
|
||||
if ((node as Text).data !== vnode.children) {
|
||||
hasMismatch = true
|
||||
|
@ -133,7 +150,7 @@ export function createHydrationFunctions(
|
|||
}
|
||||
break
|
||||
case Static:
|
||||
if (domType !== DOMNodeTypes.ELEMENT) {
|
||||
if (domType !== DOMNodeTypes.ELEMENT && domType !== DOMNodeTypes.TEXT) {
|
||||
nextNode = onMismatch()
|
||||
} else {
|
||||
// determine anchor, adopt content
|
||||
|
@ -143,7 +160,10 @@ export function createHydrationFunctions(
|
|||
const needToAdoptContent = !(vnode.children as string).length
|
||||
for (let i = 0; i < vnode.staticCount!; i++) {
|
||||
if (needToAdoptContent)
|
||||
vnode.children += (nextNode as Element).outerHTML
|
||||
vnode.children +=
|
||||
nextNode.nodeType === DOMNodeTypes.ELEMENT
|
||||
? (nextNode as Element).outerHTML
|
||||
: (nextNode as Text).data
|
||||
if (i === vnode.staticCount! - 1) {
|
||||
vnode.anchor = nextNode
|
||||
}
|
||||
|
@ -207,6 +227,15 @@ export function createHydrationFunctions(
|
|||
? locateClosingAsyncAnchor(node)
|
||||
: nextSibling(node)
|
||||
|
||||
// #4293 teleport as component root
|
||||
if (
|
||||
nextNode &&
|
||||
isComment(nextNode) &&
|
||||
nextNode.data === 'teleport end'
|
||||
) {
|
||||
nextNode = nextSibling(nextNode)
|
||||
}
|
||||
|
||||
// #3787
|
||||
// if component is async, it may get moved / unmounted before its
|
||||
// inner component is loaded, so we need to give it a placeholder
|
||||
|
|
|
@ -86,7 +86,7 @@ export { h } from './h'
|
|||
// Advanced render function utilities
|
||||
export { createVNode, cloneVNode, mergeProps, isVNode } from './vnode'
|
||||
// VNode types
|
||||
export { Fragment, Text, Comment, Static } from './vnode'
|
||||
export { Fragment, Text, Comment, Static, VNodeRef } from './vnode'
|
||||
// Built-in components
|
||||
export { Teleport, TeleportProps } from './components/Teleport'
|
||||
export { Suspense, SuspenseProps } from './components/Suspense'
|
||||
|
@ -217,6 +217,7 @@ export {
|
|||
ComponentOptionsWithArrayProps,
|
||||
ComponentCustomOptions,
|
||||
ComponentOptionsBase,
|
||||
ComponentProvideOptions,
|
||||
RenderFunction,
|
||||
MethodOptions,
|
||||
ComputedOptions,
|
||||
|
@ -324,7 +325,7 @@ const _ssrUtils = {
|
|||
}
|
||||
|
||||
/**
|
||||
* SSR utils for \@vue/server-renderer. Only exposed in cjs builds.
|
||||
* SSR utils for \@vue/server-renderer. Only exposed in ssr-possible builds.
|
||||
* @internal
|
||||
*/
|
||||
export const ssrUtils = (__SSR__ ? _ssrUtils : null) as typeof _ssrUtils
|
||||
|
|
|
@ -1064,8 +1064,12 @@ function baseCreateRenderer(
|
|||
|
||||
let { patchFlag, dynamicChildren, slotScopeIds: fragmentSlotScopeIds } = n2
|
||||
|
||||
if (__DEV__ && isHmrUpdating) {
|
||||
// HMR updated, force full diff
|
||||
if (
|
||||
__DEV__ &&
|
||||
// #5523 dev root fragment may inherit directives
|
||||
(isHmrUpdating || patchFlag & PatchFlags.DEV_ROOT_FRAGMENT)
|
||||
) {
|
||||
// HMR updated / Dev root fragment (w/ comments), force full diff
|
||||
patchFlag = 0
|
||||
optimized = false
|
||||
dynamicChildren = null
|
||||
|
@ -1098,8 +1102,6 @@ function baseCreateRenderer(
|
|||
if (
|
||||
patchFlag > 0 &&
|
||||
patchFlag & PatchFlags.STABLE_FRAGMENT &&
|
||||
// #5523 dev root fragment may inherit directives so always force update
|
||||
!(__DEV__ && patchFlag & PatchFlags.DEV_ROOT_FRAGMENT) &&
|
||||
dynamicChildren &&
|
||||
// #2715 the previous fragment could've been a BAILed one as a result
|
||||
// of renderSlot() with no valid children
|
||||
|
|
|
@ -107,7 +107,7 @@ export function setRef(
|
|||
if (hasOwn(setupState, ref)) {
|
||||
setupState[ref] = value
|
||||
}
|
||||
} else if (isRef(ref)) {
|
||||
} else if (_isRef) {
|
||||
ref.value = value
|
||||
if (rawRef.k) refs[rawRef.k] = value
|
||||
} else if (__DEV__) {
|
||||
|
|
|
@ -43,6 +43,7 @@ import { convertLegacyComponent } from './compat/component'
|
|||
import { convertLegacyVModelProps } from './compat/componentVModel'
|
||||
import { defineLegacyVNodeProperties } from './compat/renderFn'
|
||||
import { callWithAsyncErrorHandling, ErrorCodes } from './errorHandling'
|
||||
import { ComponentPublicInstance } from './componentPublicInstance'
|
||||
|
||||
export const Fragment = Symbol(__DEV__ ? 'Fragment' : undefined) as any as {
|
||||
__isFragment: true
|
||||
|
@ -68,7 +69,10 @@ export type VNodeTypes =
|
|||
export type VNodeRef =
|
||||
| string
|
||||
| Ref
|
||||
| ((ref: object | null, refs: Record<string, any>) => void)
|
||||
| ((
|
||||
ref: Element | ComponentPublicInstance | null,
|
||||
refs: Record<string, any>
|
||||
) => void)
|
||||
|
||||
export type VNodeNormalizedRefAtom = {
|
||||
i: ComponentInternalInstance
|
||||
|
|
|
@ -291,6 +291,94 @@ describe('vModel', () => {
|
|||
expect(data.lazy).toEqual('foo')
|
||||
})
|
||||
|
||||
it('should work with range', async () => {
|
||||
const component = defineComponent({
|
||||
data() {
|
||||
return { value: 25 }
|
||||
},
|
||||
render() {
|
||||
return [
|
||||
withVModel(
|
||||
h('input', {
|
||||
type: 'range',
|
||||
min: 1,
|
||||
max: 100,
|
||||
class: 'foo',
|
||||
'onUpdate:modelValue': setValue.bind(this)
|
||||
}),
|
||||
this.value,
|
||||
{
|
||||
number: true
|
||||
}
|
||||
),
|
||||
withVModel(
|
||||
h('input', {
|
||||
type: 'range',
|
||||
min: 1,
|
||||
max: 100,
|
||||
class: 'bar',
|
||||
'onUpdate:modelValue': setValue.bind(this)
|
||||
}),
|
||||
this.value,
|
||||
{
|
||||
lazy: true
|
||||
}
|
||||
)
|
||||
]
|
||||
}
|
||||
})
|
||||
render(h(component), root)
|
||||
|
||||
const foo = root.querySelector('.foo')
|
||||
const bar = root.querySelector('.bar')
|
||||
const data = root._vnode.component.data
|
||||
|
||||
foo.value = 20
|
||||
triggerEvent('input', foo)
|
||||
await nextTick()
|
||||
expect(data.value).toEqual(20)
|
||||
|
||||
foo.value = 200
|
||||
triggerEvent('input', foo)
|
||||
await nextTick()
|
||||
expect(data.value).toEqual(100)
|
||||
|
||||
foo.value = -1
|
||||
triggerEvent('input', foo)
|
||||
await nextTick()
|
||||
expect(data.value).toEqual(1)
|
||||
|
||||
bar.value = 30
|
||||
triggerEvent('change', bar)
|
||||
await nextTick()
|
||||
expect(data.value).toEqual('30')
|
||||
|
||||
bar.value = 200
|
||||
triggerEvent('change', bar)
|
||||
await nextTick()
|
||||
expect(data.value).toEqual('100')
|
||||
|
||||
bar.value = -1
|
||||
triggerEvent('change', bar)
|
||||
await nextTick()
|
||||
expect(data.value).toEqual('1')
|
||||
|
||||
data.value = 60
|
||||
await nextTick()
|
||||
expect(foo.value).toEqual('60')
|
||||
expect(bar.value).toEqual('60')
|
||||
|
||||
data.value = -1
|
||||
await nextTick()
|
||||
expect(foo.value).toEqual('1')
|
||||
expect(bar.value).toEqual('1')
|
||||
|
||||
data.value = 200
|
||||
await nextTick()
|
||||
expect(foo.value).toEqual('100')
|
||||
expect(bar.value).toEqual('100')
|
||||
})
|
||||
|
||||
it('should work with checkbox', async () => {
|
||||
const component = defineComponent({
|
||||
data() {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@vue/runtime-dom",
|
||||
"version": "3.2.33",
|
||||
"version": "3.2.37",
|
||||
"description": "@vue/runtime-dom",
|
||||
"main": "index.js",
|
||||
"module": "dist/runtime-dom.esm-bundler.js",
|
||||
|
@ -35,8 +35,8 @@
|
|||
},
|
||||
"homepage": "https://github.com/vuejs/core/tree/main/packages/runtime-dom#readme",
|
||||
"dependencies": {
|
||||
"@vue/shared": "3.2.33",
|
||||
"@vue/runtime-core": "3.2.33",
|
||||
"@vue/shared": "3.2.37",
|
||||
"@vue/runtime-core": "3.2.37",
|
||||
"csstype": "^2.6.8"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -174,9 +174,11 @@ export function resolveTransitionProps(
|
|||
done && done()
|
||||
}
|
||||
|
||||
let isLeaving = false
|
||||
const finishLeave = (el: Element, done?: () => void) => {
|
||||
isLeaving = false
|
||||
const finishLeave = (
|
||||
el: Element & { _isLeaving?: boolean },
|
||||
done?: () => void
|
||||
) => {
|
||||
el._isLeaving = false
|
||||
removeTransitionClass(el, leaveFromClass)
|
||||
removeTransitionClass(el, leaveToClass)
|
||||
removeTransitionClass(el, leaveActiveClass)
|
||||
|
@ -223,8 +225,8 @@ export function resolveTransitionProps(
|
|||
},
|
||||
onEnter: makeEnterHook(false),
|
||||
onAppear: makeEnterHook(true),
|
||||
onLeave(el, done) {
|
||||
isLeaving = true
|
||||
onLeave(el: Element & { _isLeaving?: boolean }, done) {
|
||||
el._isLeaving = true
|
||||
const resolve = () => finishLeave(el, done)
|
||||
addTransitionClass(el, leaveFromClass)
|
||||
if (__COMPAT__ && legacyClassEnabled) {
|
||||
|
@ -234,7 +236,7 @@ export function resolveTransitionProps(
|
|||
forceReflow()
|
||||
addTransitionClass(el, leaveActiveClass)
|
||||
nextFrame(() => {
|
||||
if (!isLeaving) {
|
||||
if (!el._isLeaving) {
|
||||
// cancelled
|
||||
return
|
||||
}
|
||||
|
|
|
@ -269,6 +269,24 @@ export const vModelDynamic: ObjectDirective<
|
|||
}
|
||||
}
|
||||
|
||||
function resolveDynamicModel(tagName: string, type: string | undefined) {
|
||||
switch (tagName) {
|
||||
case 'SELECT':
|
||||
return vModelSelect
|
||||
case 'TEXTAREA':
|
||||
return vModelText
|
||||
default:
|
||||
switch (type) {
|
||||
case 'checkbox':
|
||||
return vModelCheckbox
|
||||
case 'radio':
|
||||
return vModelRadio
|
||||
default:
|
||||
return vModelText
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function callModelHook(
|
||||
el: HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement,
|
||||
binding: DirectiveBinding,
|
||||
|
@ -276,26 +294,10 @@ function callModelHook(
|
|||
prevVNode: VNode | null,
|
||||
hook: keyof ObjectDirective
|
||||
) {
|
||||
let modelToUse: ObjectDirective
|
||||
switch (el.tagName) {
|
||||
case 'SELECT':
|
||||
modelToUse = vModelSelect
|
||||
break
|
||||
case 'TEXTAREA':
|
||||
modelToUse = vModelText
|
||||
break
|
||||
default:
|
||||
switch (vnode.props && vnode.props.type) {
|
||||
case 'checkbox':
|
||||
modelToUse = vModelCheckbox
|
||||
break
|
||||
case 'radio':
|
||||
modelToUse = vModelRadio
|
||||
break
|
||||
default:
|
||||
modelToUse = vModelText
|
||||
}
|
||||
}
|
||||
const modelToUse = resolveDynamicModel(
|
||||
el.tagName,
|
||||
vnode.props && vnode.props.type
|
||||
)
|
||||
const fn = modelToUse[hook] as DirectiveHook
|
||||
fn && fn(el, binding, vnode, prevVNode)
|
||||
}
|
||||
|
@ -324,4 +326,18 @@ export function initVModelForSSR() {
|
|||
return { checked: true }
|
||||
}
|
||||
}
|
||||
|
||||
vModelDynamic.getSSRProps = (binding, vnode) => {
|
||||
if (typeof vnode.type !== 'string') {
|
||||
return
|
||||
}
|
||||
const modelToUse = resolveDynamicModel(
|
||||
// resolveDynamicModel expects an uppercase tag name, but vnode.type is lowercase
|
||||
vnode.type.toUpperCase(),
|
||||
vnode.props && vnode.props.type
|
||||
)
|
||||
if (modelToUse.getSSRProps) {
|
||||
return modelToUse.getSSRProps(binding, vnode)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,7 +25,7 @@ const [_getNow, skipTimestampCheck] = /*#__PURE__*/ (() => {
|
|||
// if the low-res timestamp which is bigger than the event timestamp
|
||||
// (which is evaluated AFTER) it means the event is using a hi-res timestamp,
|
||||
// and we need to use the hi-res version for event listeners as well.
|
||||
_getNow = () => performance.now()
|
||||
_getNow = performance.now.bind(performance)
|
||||
}
|
||||
// #3485: Firefox <= 53 has incorrect Event.timeStamp implementation
|
||||
// and does not fire microtasks in between event propagation, so safe to exclude.
|
||||
|
|
|
@ -40,6 +40,7 @@ export interface CSSProperties
|
|||
* For examples and more information, visit:
|
||||
* https://github.com/frenic/csstype#what-should-i-do-when-i-get-type-errors
|
||||
*/
|
||||
[v: `--${string}`]: string | number | undefined
|
||||
}
|
||||
|
||||
type Booleanish = boolean | 'true' | 'false'
|
||||
|
@ -457,7 +458,7 @@ export interface InputHTMLAttributes extends HTMLAttributes {
|
|||
autocomplete?: string
|
||||
autofocus?: Booleanish
|
||||
capture?: boolean | 'user' | 'environment' // https://www.w3.org/tr/html-media-capture/#the-capture-attribute
|
||||
checked?: Booleanish | any[] // for IDE v-model multi-checkbox support
|
||||
checked?: Booleanish | any[] | Set<any> // for IDE v-model multi-checkbox support
|
||||
crossorigin?: string
|
||||
disabled?: Booleanish
|
||||
form?: string
|
||||
|
@ -1309,10 +1310,7 @@ import * as RuntimeCore from '@vue/runtime-core'
|
|||
|
||||
type ReservedProps = {
|
||||
key?: string | number | symbol
|
||||
ref?:
|
||||
| string
|
||||
| RuntimeCore.Ref
|
||||
| ((ref: Element | RuntimeCore.ComponentPublicInstance | null) => void)
|
||||
ref?: RuntimeCore.VNodeRef
|
||||
ref_for?: boolean
|
||||
ref_key?: string
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@vue/runtime-test",
|
||||
"version": "3.2.33",
|
||||
"version": "3.2.37",
|
||||
"description": "@vue/runtime-test",
|
||||
"private": true,
|
||||
"main": "index.js",
|
||||
|
@ -25,7 +25,7 @@
|
|||
},
|
||||
"homepage": "https://github.com/vuejs/core/tree/main/packages/runtime-test#readme",
|
||||
"dependencies": {
|
||||
"@vue/shared": "3.2.33",
|
||||
"@vue/runtime-core": "3.2.33"
|
||||
"@vue/shared": "3.2.37",
|
||||
"@vue/runtime-core": "3.2.37"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -676,7 +676,7 @@ function testRender(type: string, render: typeof renderToString) {
|
|||
render: () => h('p', 'hello')
|
||||
}
|
||||
expect(await render(h(KeepAlive, () => h(MyComp)))).toBe(
|
||||
`<!--[--><p>hello</p><!--]-->`
|
||||
`<p>hello</p>`
|
||||
)
|
||||
})
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@ import {
|
|||
vModelText,
|
||||
vModelRadio,
|
||||
vModelCheckbox,
|
||||
vModelDynamic,
|
||||
resolveDirective
|
||||
} from 'vue'
|
||||
import { ssrGetDirectiveProps, ssrRenderAttrs } from '../src'
|
||||
|
@ -376,6 +377,100 @@ describe('ssr: directives', () => {
|
|||
})
|
||||
})
|
||||
|
||||
describe('vnode v-model dynamic', () => {
|
||||
test('text', async () => {
|
||||
expect(
|
||||
await renderToString(
|
||||
createApp({
|
||||
render() {
|
||||
return withDirectives(h('input'), [[vModelDynamic, 'hello']])
|
||||
}
|
||||
})
|
||||
)
|
||||
).toBe(`<input value="hello">`)
|
||||
})
|
||||
|
||||
test('radio', async () => {
|
||||
expect(
|
||||
await renderToString(
|
||||
createApp({
|
||||
render() {
|
||||
return withDirectives(
|
||||
h('input', { type: 'radio', value: 'hello' }),
|
||||
[[vModelDynamic, 'hello']]
|
||||
)
|
||||
}
|
||||
})
|
||||
)
|
||||
).toBe(`<input type="radio" value="hello" checked>`)
|
||||
|
||||
expect(
|
||||
await renderToString(
|
||||
createApp({
|
||||
render() {
|
||||
return withDirectives(
|
||||
h('input', { type: 'radio', value: 'hello' }),
|
||||
[[vModelDynamic, 'foo']]
|
||||
)
|
||||
}
|
||||
})
|
||||
)
|
||||
).toBe(`<input type="radio" value="hello">`)
|
||||
})
|
||||
|
||||
test('checkbox', async () => {
|
||||
expect(
|
||||
await renderToString(
|
||||
createApp({
|
||||
render() {
|
||||
return withDirectives(h('input', { type: 'checkbox' }), [
|
||||
[vModelDynamic, true]
|
||||
])
|
||||
}
|
||||
})
|
||||
)
|
||||
).toBe(`<input type="checkbox" checked>`)
|
||||
|
||||
expect(
|
||||
await renderToString(
|
||||
createApp({
|
||||
render() {
|
||||
return withDirectives(h('input', { type: 'checkbox' }), [
|
||||
[vModelDynamic, false]
|
||||
])
|
||||
}
|
||||
})
|
||||
)
|
||||
).toBe(`<input type="checkbox">`)
|
||||
|
||||
expect(
|
||||
await renderToString(
|
||||
createApp({
|
||||
render() {
|
||||
return withDirectives(
|
||||
h('input', { type: 'checkbox', value: 'foo' }),
|
||||
[[vModelDynamic, ['foo']]]
|
||||
)
|
||||
}
|
||||
})
|
||||
)
|
||||
).toBe(`<input type="checkbox" value="foo" checked>`)
|
||||
|
||||
expect(
|
||||
await renderToString(
|
||||
createApp({
|
||||
render() {
|
||||
return withDirectives(
|
||||
h('input', { type: 'checkbox', value: 'foo' }),
|
||||
[[vModelDynamic, []]]
|
||||
)
|
||||
}
|
||||
})
|
||||
)
|
||||
).toBe(`<input type="checkbox" value="foo">`)
|
||||
})
|
||||
})
|
||||
|
||||
test('custom directive w/ getSSRProps (vdom)', async () => {
|
||||
expect(
|
||||
await renderToString(
|
||||
|
|
|
@ -0,0 +1,144 @@
|
|||
/**
|
||||
* @jest-environment node
|
||||
*/
|
||||
|
||||
import { createApp } from 'vue'
|
||||
import { renderToString } from '../src/renderToString'
|
||||
|
||||
const components = {
|
||||
one: {
|
||||
template: `<div><slot/></div>`
|
||||
}
|
||||
}
|
||||
|
||||
describe('ssr: slot', () => {
|
||||
test('text slot', async () => {
|
||||
expect(
|
||||
await renderToString(
|
||||
createApp({
|
||||
components,
|
||||
template: `<one>hello</one>`
|
||||
})
|
||||
)
|
||||
).toBe(`<div><!--[-->hello<!--]--></div>`)
|
||||
})
|
||||
|
||||
test('element slot', async () => {
|
||||
expect(
|
||||
await renderToString(
|
||||
createApp({
|
||||
components,
|
||||
template: `<one><div>hi</div></one>`
|
||||
})
|
||||
)
|
||||
).toBe(`<div><!--[--><div>hi</div><!--]--></div>`)
|
||||
})
|
||||
|
||||
test('empty slot', async () => {
|
||||
expect(
|
||||
await renderToString(
|
||||
createApp({
|
||||
components: {
|
||||
one: {
|
||||
template: `<div><slot/></div>`
|
||||
}
|
||||
},
|
||||
template: `<one><template v-if="false"/></one>`
|
||||
})
|
||||
)
|
||||
).toBe(`<div><!--[--><!--]--></div>`)
|
||||
})
|
||||
|
||||
test('empty slot (manual comments)', async () => {
|
||||
expect(
|
||||
await renderToString(
|
||||
createApp({
|
||||
components: {
|
||||
one: {
|
||||
template: `<div><slot/></div>`
|
||||
}
|
||||
},
|
||||
template: `<one><!--hello--></one>`
|
||||
})
|
||||
)
|
||||
).toBe(`<div><!--[--><!--]--></div>`)
|
||||
})
|
||||
|
||||
test('empty slot (multi-line comments)', async () => {
|
||||
expect(
|
||||
await renderToString(
|
||||
createApp({
|
||||
components: {
|
||||
one: {
|
||||
template: `<div><slot/></div>`
|
||||
}
|
||||
},
|
||||
template: `<one><!--he\nllo--></one>`
|
||||
})
|
||||
)
|
||||
).toBe(`<div><!--[--><!--]--></div>`)
|
||||
})
|
||||
|
||||
test('multiple elements', async () => {
|
||||
expect(
|
||||
await renderToString(
|
||||
createApp({
|
||||
components,
|
||||
template: `<one><div>one</div><div>two</div></one>`
|
||||
})
|
||||
)
|
||||
).toBe(`<div><!--[--><div>one</div><div>two</div><!--]--></div>`)
|
||||
})
|
||||
|
||||
test('fragment slot (template v-if)', async () => {
|
||||
expect(
|
||||
await renderToString(
|
||||
createApp({
|
||||
components,
|
||||
template: `<one><template v-if="true">hello</template></one>`
|
||||
})
|
||||
)
|
||||
).toBe(`<div><!--[--><!--[-->hello<!--]--><!--]--></div>`)
|
||||
})
|
||||
|
||||
test('fragment slot (template v-if + multiple elements)', async () => {
|
||||
expect(
|
||||
await renderToString(
|
||||
createApp({
|
||||
components,
|
||||
template: `<one><template v-if="true"><div>one</div><div>two</div></template></one>`
|
||||
})
|
||||
)
|
||||
).toBe(
|
||||
`<div><!--[--><!--[--><div>one</div><div>two</div><!--]--><!--]--></div>`
|
||||
)
|
||||
})
|
||||
|
||||
test('transition slot', async () => {
|
||||
expect(
|
||||
await renderToString(
|
||||
createApp({
|
||||
components: {
|
||||
one: {
|
||||
template: `<transition><slot/></transition>`
|
||||
}
|
||||
},
|
||||
template: `<one><div v-if="false">foo</div></one>`
|
||||
})
|
||||
)
|
||||
).toBe(`<!---->`)
|
||||
|
||||
expect(
|
||||
await renderToString(
|
||||
createApp({
|
||||
components: {
|
||||
one: {
|
||||
template: `<transition><slot/></transition>`
|
||||
}
|
||||
},
|
||||
template: `<one><div v-if="true">foo</div></one>`
|
||||
})
|
||||
)
|
||||
).toBe(`<div>foo</div>`)
|
||||
})
|
||||
})
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
import { createApp, h, Teleport } from 'vue'
|
||||
import { renderToString } from '../src/renderToString'
|
||||
import { renderToSimpleStream } from '../src/renderToStream'
|
||||
import { SSRContext } from '../src/render'
|
||||
import { ssrRenderTeleport } from '../src/helpers/ssrRenderTeleport'
|
||||
|
||||
|
@ -30,7 +31,9 @@ describe('ssrRenderTeleport', () => {
|
|||
ctx
|
||||
)
|
||||
expect(html).toBe('<!--teleport start--><!--teleport end-->')
|
||||
expect(ctx.teleports!['#target']).toBe(`<div>content</div><!---->`)
|
||||
expect(ctx.teleports!['#target']).toBe(
|
||||
`<div>content</div><!--teleport anchor-->`
|
||||
)
|
||||
})
|
||||
|
||||
test('teleport rendering (compiled + disabled)', async () => {
|
||||
|
@ -57,7 +60,7 @@ describe('ssrRenderTeleport', () => {
|
|||
expect(html).toBe(
|
||||
'<!--teleport start--><div>content</div><!--teleport end-->'
|
||||
)
|
||||
expect(ctx.teleports!['#target']).toBe(`<!---->`)
|
||||
expect(ctx.teleports!['#target']).toBe(`<!--teleport anchor-->`)
|
||||
})
|
||||
|
||||
test('teleport rendering (vnode)', async () => {
|
||||
|
@ -73,7 +76,9 @@ describe('ssrRenderTeleport', () => {
|
|||
ctx
|
||||
)
|
||||
expect(html).toBe('<!--teleport start--><!--teleport end-->')
|
||||
expect(ctx.teleports!['#target']).toBe('<span>hello</span><!---->')
|
||||
expect(ctx.teleports!['#target']).toBe(
|
||||
'<span>hello</span><!--teleport anchor-->'
|
||||
)
|
||||
})
|
||||
|
||||
test('teleport rendering (vnode + disabled)', async () => {
|
||||
|
@ -92,7 +97,7 @@ describe('ssrRenderTeleport', () => {
|
|||
expect(html).toBe(
|
||||
'<!--teleport start--><span>hello</span><!--teleport end-->'
|
||||
)
|
||||
expect(ctx.teleports!['#target']).toBe(`<!---->`)
|
||||
expect(ctx.teleports!['#target']).toBe(`<!--teleport anchor-->`)
|
||||
})
|
||||
|
||||
test('multiple teleports with same target', async () => {
|
||||
|
@ -114,7 +119,7 @@ describe('ssrRenderTeleport', () => {
|
|||
'<div><!--teleport start--><!--teleport end--><!--teleport start--><!--teleport end--></div>'
|
||||
)
|
||||
expect(ctx.teleports!['#target']).toBe(
|
||||
'<span>hello</span><!---->world<!---->'
|
||||
'<span>hello</span><!--teleport anchor-->world<!--teleport anchor-->'
|
||||
)
|
||||
})
|
||||
|
||||
|
@ -132,6 +137,43 @@ describe('ssrRenderTeleport', () => {
|
|||
ctx
|
||||
)
|
||||
expect(html).toBe('<!--teleport start--><!--teleport end-->')
|
||||
expect(ctx.teleports!['#target']).toBe(`<div>content</div><!---->`)
|
||||
expect(ctx.teleports!['#target']).toBe(
|
||||
`<div>content</div><!--teleport anchor-->`
|
||||
)
|
||||
})
|
||||
|
||||
test('teleport inside async component (stream)', async () => {
|
||||
const ctx: SSRContext = {}
|
||||
const asyncComponent = {
|
||||
template: '<teleport to="#target"><div>content</div></teleport>',
|
||||
async setup() {}
|
||||
}
|
||||
let html = ''
|
||||
let resolve: any
|
||||
const p = new Promise(r => (resolve = r))
|
||||
renderToSimpleStream(
|
||||
h({
|
||||
template: '<async-component />',
|
||||
components: { asyncComponent }
|
||||
}),
|
||||
ctx,
|
||||
{
|
||||
push(chunk) {
|
||||
if (chunk === null) {
|
||||
resolve()
|
||||
} else {
|
||||
html += chunk
|
||||
}
|
||||
},
|
||||
destroy(err) {
|
||||
throw err
|
||||
}
|
||||
}
|
||||
)
|
||||
await p
|
||||
expect(html).toBe('<!--teleport start--><!--teleport end-->')
|
||||
expect(ctx.teleports!['#target']).toBe(
|
||||
`<div>content</div><!--teleport anchor-->`
|
||||
)
|
||||
})
|
||||
})
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@vue/server-renderer",
|
||||
"version": "3.2.33",
|
||||
"version": "3.2.37",
|
||||
"description": "@vue/server-renderer",
|
||||
"main": "index.js",
|
||||
"module": "dist/server-renderer.esm-bundler.js",
|
||||
|
@ -13,6 +13,7 @@
|
|||
"name": "VueServerRenderer",
|
||||
"formats": [
|
||||
"esm-bundler",
|
||||
"esm-browser",
|
||||
"cjs"
|
||||
]
|
||||
},
|
||||
|
@ -31,10 +32,10 @@
|
|||
},
|
||||
"homepage": "https://github.com/vuejs/core/tree/main/packages/server-renderer#readme",
|
||||
"peerDependencies": {
|
||||
"vue": "3.2.33"
|
||||
"vue": "3.2.37"
|
||||
},
|
||||
"dependencies": {
|
||||
"@vue/shared": "3.2.33",
|
||||
"@vue/compiler-ssr": "3.2.33"
|
||||
"@vue/shared": "3.2.37",
|
||||
"@vue/compiler-ssr": "3.2.37"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -40,7 +40,8 @@ export function ssrRenderSlotInner(
|
|||
fallbackRenderFn: (() => void) | null,
|
||||
push: PushFn,
|
||||
parentComponent: ComponentInternalInstance,
|
||||
slotScopeId?: string
|
||||
slotScopeId?: string,
|
||||
transition?: boolean
|
||||
) {
|
||||
const slotFn = slots[slotName]
|
||||
if (slotFn) {
|
||||
|
@ -61,10 +62,14 @@ export function ssrRenderSlotInner(
|
|||
// ssr slot.
|
||||
// check if the slot renders all comments, in which case use the fallback
|
||||
let isEmptySlot = true
|
||||
for (let i = 0; i < slotBuffer.length; i++) {
|
||||
if (!isComment(slotBuffer[i])) {
|
||||
isEmptySlot = false
|
||||
break
|
||||
if (transition) {
|
||||
isEmptySlot = false
|
||||
} else {
|
||||
for (let i = 0; i < slotBuffer.length; i++) {
|
||||
if (!isComment(slotBuffer[i])) {
|
||||
isEmptySlot = false
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if (isEmptySlot) {
|
||||
|
@ -82,7 +87,11 @@ export function ssrRenderSlotInner(
|
|||
}
|
||||
}
|
||||
|
||||
const commentRE = /^<!--.*-->$/
|
||||
const commentTestRE = /^<!--.*-->$/s
|
||||
const commentRE = /<!--[^]*?-->/gm
|
||||
function isComment(item: SSRBufferItem) {
|
||||
return typeof item === 'string' && commentRE.test(item)
|
||||
if (typeof item !== 'string' || !commentTestRE.test(item)) return false
|
||||
// if item is '<!---->' or '<!--[-->' or '<!--]-->', return true directly
|
||||
if (item.length <= 8) return true
|
||||
return !item.replace(commentRE, '').trim()
|
||||
}
|
||||
|
|
|
@ -10,28 +10,28 @@ export function ssrRenderTeleport(
|
|||
) {
|
||||
parentPush('<!--teleport start-->')
|
||||
|
||||
let teleportContent: SSRBufferItem
|
||||
|
||||
if (disabled) {
|
||||
contentRenderFn(parentPush)
|
||||
teleportContent = `<!---->`
|
||||
} else {
|
||||
const { getBuffer, push } = createBuffer()
|
||||
contentRenderFn(push)
|
||||
push(`<!---->`) // teleport end anchor
|
||||
teleportContent = getBuffer()
|
||||
}
|
||||
|
||||
const context = parentComponent.appContext.provides[
|
||||
ssrContextKey as any
|
||||
] as SSRContext
|
||||
const teleportBuffers =
|
||||
context.__teleportBuffers || (context.__teleportBuffers = {})
|
||||
if (teleportBuffers[target]) {
|
||||
teleportBuffers[target].push(teleportContent)
|
||||
const targetBuffer = teleportBuffers[target] || (teleportBuffers[target] = [])
|
||||
// record current index of the target buffer to handle nested teleports
|
||||
// since the parent needs to be rendered before the child
|
||||
const bufferIndex = targetBuffer.length
|
||||
|
||||
let teleportContent: SSRBufferItem
|
||||
|
||||
if (disabled) {
|
||||
contentRenderFn(parentPush)
|
||||
teleportContent = `<!--teleport anchor-->`
|
||||
} else {
|
||||
teleportBuffers[target] = [teleportContent]
|
||||
const { getBuffer, push } = createBuffer()
|
||||
contentRenderFn(push)
|
||||
push(`<!--teleport anchor-->`)
|
||||
teleportContent = getBuffer()
|
||||
}
|
||||
|
||||
targetBuffer.splice(bufferIndex, 0, teleportContent)
|
||||
parentPush('<!--teleport end-->')
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ import {
|
|||
ComponentInternalInstance,
|
||||
DirectiveBinding,
|
||||
Fragment,
|
||||
FunctionalComponent,
|
||||
mergeProps,
|
||||
ssrUtils,
|
||||
Static,
|
||||
|
@ -112,12 +113,17 @@ function renderComponentSubTree(
|
|||
const comp = instance.type as Component
|
||||
const { getBuffer, push } = createBuffer()
|
||||
if (isFunction(comp)) {
|
||||
renderVNode(
|
||||
push,
|
||||
(instance.subTree = renderComponentRoot(instance)),
|
||||
instance,
|
||||
slotScopeId
|
||||
)
|
||||
let root = renderComponentRoot(instance)
|
||||
// #5817 scope ID attrs not falling through if functional component doesn't
|
||||
// have props
|
||||
if (!(comp as FunctionalComponent).props) {
|
||||
for (const key in instance.attrs) {
|
||||
if (key.startsWith(`data-v-`)) {
|
||||
;(root.props || (root.props = {}))[key] = ``
|
||||
}
|
||||
}
|
||||
}
|
||||
renderVNode(push, (instance.subTree = root), instance, slotScopeId)
|
||||
} else {
|
||||
if (
|
||||
(!instance.render || instance.render === NOOP) &&
|
||||
|
|
|
@ -9,6 +9,7 @@ import {
|
|||
import { isString, isPromise } from '@vue/shared'
|
||||
import { renderComponentVNode, SSRBuffer, SSRContext } from './render'
|
||||
import { Readable, Writable } from 'stream'
|
||||
import { resolveTeleports } from './renderToString'
|
||||
|
||||
const { isVNode } = ssrUtils
|
||||
|
||||
|
@ -74,6 +75,7 @@ export function renderToSimpleStream<T extends SimpleReadable>(
|
|||
|
||||
Promise.resolve(renderComponentVNode(vnode))
|
||||
.then(buffer => unrollBuffer(buffer, stream))
|
||||
.then(() => resolveTeleports(context))
|
||||
.then(() => stream.push(null))
|
||||
.catch(error => {
|
||||
stream.destroy(error)
|
||||
|
|
|
@ -70,7 +70,7 @@ export async function renderToString(
|
|||
return result
|
||||
}
|
||||
|
||||
async function resolveTeleports(context: SSRContext) {
|
||||
export async function resolveTeleports(context: SSRContext) {
|
||||
if (context.__teleportBuffers) {
|
||||
context.teleports = context.teleports || {}
|
||||
for (const key in context.__teleportBuffers) {
|
||||
|
|
|
@ -9,10 +9,10 @@
|
|||
<script>
|
||||
// process shim for old versions of @vue/compiler-sfc dependency
|
||||
window.process = { env: {} }
|
||||
const saved = localStorage.getItem('vue-sfc-playground-prefer-dark')
|
||||
const savedPreferDark = localStorage.getItem('vue-sfc-playground-prefer-dark')
|
||||
if (
|
||||
saved !== 'false' ||
|
||||
window.matchMedia('(prefers-color-scheme: dark)').matches
|
||||
savedPreferDark === 'true' ||
|
||||
(!savedPreferDark && window.matchMedia('(prefers-color-scheme: dark)').matches)
|
||||
) {
|
||||
document.documentElement.classList.add('dark')
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@vue/sfc-playground",
|
||||
"version": "3.2.33",
|
||||
"version": "3.2.37",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
@ -12,8 +12,8 @@
|
|||
"vite": "^2.9.8"
|
||||
},
|
||||
"dependencies": {
|
||||
"vue": "3.2.33",
|
||||
"@vue/repl": "^1.0.0",
|
||||
"vue": "3.2.37",
|
||||
"@vue/repl": "^1.3.0",
|
||||
"file-saver": "^2.0.5",
|
||||
"jszip": "^3.6.0"
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<script setup lang="ts">
|
||||
import Header from './Header.vue'
|
||||
import { Repl, ReplStore } from '@vue/repl'
|
||||
import { watchEffect } from 'vue'
|
||||
import { ref, watchEffect } from 'vue'
|
||||
|
||||
const setVH = () => {
|
||||
document.documentElement.style.setProperty('--vh', window.innerHeight + `px`)
|
||||
|
@ -9,29 +9,70 @@ const setVH = () => {
|
|||
window.addEventListener('resize', setVH)
|
||||
setVH()
|
||||
|
||||
const useDevMode = ref(false)
|
||||
const useSSRMode = ref(false)
|
||||
|
||||
let hash = location.hash.slice(1)
|
||||
if (hash.startsWith('__DEV__')) {
|
||||
hash = hash.slice(7)
|
||||
useDevMode.value = true
|
||||
}
|
||||
if (hash.startsWith('__SSR__')) {
|
||||
hash = hash.slice(7)
|
||||
useSSRMode.value = true
|
||||
}
|
||||
|
||||
const store = new ReplStore({
|
||||
serializedState: location.hash.slice(1),
|
||||
serializedState: hash,
|
||||
defaultVueRuntimeURL: import.meta.env.PROD
|
||||
? `${location.origin}/vue.runtime.esm-browser.js`
|
||||
: `${location.origin}/src/vue-dev-proxy`
|
||||
: `${location.origin}/src/vue-dev-proxy`,
|
||||
defaultVueServerRendererURL: import.meta.env.PROD
|
||||
? `${location.origin}/server-renderer.esm-browser.js`
|
||||
: `${location.origin}/src/vue-server-renderer-dev-proxy`
|
||||
})
|
||||
|
||||
// enable experimental features
|
||||
const sfcOptions = {
|
||||
script: {
|
||||
inlineTemplate: !useDevMode.value,
|
||||
reactivityTransform: true
|
||||
}
|
||||
}
|
||||
|
||||
// persist state
|
||||
watchEffect(() => history.replaceState({}, '', store.serialize()))
|
||||
watchEffect(() => {
|
||||
const newHash = store
|
||||
.serialize()
|
||||
.replace(/^#/, useSSRMode.value ? `#__SSR__` : `#`)
|
||||
.replace(/^#/, useDevMode.value ? `#__DEV__` : `#`)
|
||||
history.replaceState({}, '', newHash)
|
||||
})
|
||||
|
||||
function toggleDevMode() {
|
||||
const dev = (useDevMode.value = !useDevMode.value)
|
||||
sfcOptions.script.inlineTemplate = !dev
|
||||
store.setFiles(store.getFiles())
|
||||
}
|
||||
|
||||
function toggleSSR() {
|
||||
useSSRMode.value = !useSSRMode.value
|
||||
store.setFiles(store.getFiles())
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Header :store="store" />
|
||||
<Header
|
||||
:store="store"
|
||||
:dev="useDevMode"
|
||||
:ssr="useSSRMode"
|
||||
@toggle-dev="toggleDevMode"
|
||||
@toggle-ssr="toggleSSR"
|
||||
/>
|
||||
<Repl
|
||||
@keydown.ctrl.s.prevent
|
||||
@keydown.meta.s.prevent
|
||||
:ssr="useSSRMode"
|
||||
:store="store"
|
||||
:showCompileOutput="true"
|
||||
:autoResize="true"
|
||||
|
@ -41,6 +82,10 @@ watchEffect(() => history.replaceState({}, '', store.serialize()))
|
|||
</template>
|
||||
|
||||
<style>
|
||||
.dark {
|
||||
color-scheme: dark;
|
||||
}
|
||||
|
||||
body {
|
||||
font-size: 13px;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen,
|
||||
|
|
|
@ -8,7 +8,8 @@ import Download from './icons/Download.vue'
|
|||
import GitHub from './icons/GitHub.vue'
|
||||
|
||||
// @ts-ignore
|
||||
const { store } = defineProps(['store'])
|
||||
const props = defineProps(['store', 'dev', 'ssr'])
|
||||
const { store } = props
|
||||
|
||||
const currentCommit = __COMMIT__
|
||||
const activeVersion = ref(`@${currentCommit}`)
|
||||
|
@ -53,6 +54,11 @@ onMounted(async () => {
|
|||
window.addEventListener('click', () => {
|
||||
expanded.value = false
|
||||
})
|
||||
window.addEventListener('blur', () => {
|
||||
if (document.activeElement?.tagName === 'IFRAME') {
|
||||
expanded.value = false
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
async function fetchVersions(): Promise<string[]> {
|
||||
|
@ -93,7 +99,8 @@ async function fetchVersions(): Promise<string[]> {
|
|||
<div class="links">
|
||||
<div class="version" @click.stop>
|
||||
<span class="active-version" @click="toggle">
|
||||
Version: {{ activeVersion }}
|
||||
Version
|
||||
<span class="number">{{ activeVersion }}</span>
|
||||
</span>
|
||||
<ul class="versions" :class="{ expanded }">
|
||||
<li v-if="!publishedVersions"><a>loading versions...</a></li>
|
||||
|
@ -112,6 +119,22 @@ async function fetchVersions(): Promise<string[]> {
|
|||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<button
|
||||
title="Toggle development production mode"
|
||||
class="toggle-dev"
|
||||
:class="{ dev }"
|
||||
@click="$emit('toggle-dev')"
|
||||
>
|
||||
<span>{{ dev ? 'DEV' : 'PROD' }}</span>
|
||||
</button>
|
||||
<button
|
||||
title="Toggle server rendering mode"
|
||||
class="toggle-ssr"
|
||||
:class="{ enabled: ssr }"
|
||||
@click="$emit('toggle-ssr')"
|
||||
>
|
||||
<span>{{ ssr ? 'SSR ON' : 'SSR OFF' }}</span>
|
||||
</button>
|
||||
<button title="Toggle dark mode" class="toggle-dark" @click="toggleDark">
|
||||
<Sun class="light" />
|
||||
<Moon class="dark" />
|
||||
|
@ -126,13 +149,10 @@ async function fetchVersions(): Promise<string[]> {
|
|||
>
|
||||
<Download />
|
||||
</button>
|
||||
<button
|
||||
title="View on GitHub"
|
||||
class="github"
|
||||
>
|
||||
<button title="View on GitHub" class="github">
|
||||
<a
|
||||
href="https://github.com/vuejs/core/tree/main/packages/sfc-playground"
|
||||
target="_blank"
|
||||
href="https://github.com/vuejs/core/tree/main/packages/sfc-playground"
|
||||
target="_blank"
|
||||
>
|
||||
<GitHub />
|
||||
</a>
|
||||
|
@ -146,6 +166,11 @@ nav {
|
|||
--bg: #fff;
|
||||
--bg-light: #fff;
|
||||
--border: #ddd;
|
||||
--btn: #666;
|
||||
--highlight: #333;
|
||||
--green: #3ca877;
|
||||
--purple: #904cbc;
|
||||
--btn-bg: #eee;
|
||||
|
||||
color: var(--base);
|
||||
height: var(--nav-height);
|
||||
|
@ -164,25 +189,22 @@ nav {
|
|||
--bg: #1a1a1a;
|
||||
--bg-light: #242424;
|
||||
--border: #383838;
|
||||
--highlight: #fff;
|
||||
--btn-bg: #333;
|
||||
|
||||
box-shadow: none;
|
||||
border-bottom: 1px solid var(--border);
|
||||
}
|
||||
|
||||
h1 {
|
||||
margin: 0;
|
||||
line-height: var(--nav-height);
|
||||
font-weight: 500;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
display: inline-flex;
|
||||
place-items: center;
|
||||
}
|
||||
|
||||
h1 img {
|
||||
height: 24px;
|
||||
vertical-align: middle;
|
||||
margin-right: 10px;
|
||||
position: relative;
|
||||
top: -2px;
|
||||
}
|
||||
|
||||
@media (max-width: 560px) {
|
||||
|
@ -202,7 +224,6 @@ h1 img {
|
|||
}
|
||||
|
||||
.version {
|
||||
display: inline-block;
|
||||
margin-right: 12px;
|
||||
position: relative;
|
||||
}
|
||||
|
@ -210,28 +231,53 @@ h1 img {
|
|||
.active-version {
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
line-height: var(--nav-height);
|
||||
padding-right: 15px;
|
||||
display: inline-flex;
|
||||
place-items: center;
|
||||
}
|
||||
|
||||
.active-version:after {
|
||||
.active-version .number {
|
||||
color: var(--green);
|
||||
margin-left: 4px;
|
||||
}
|
||||
|
||||
.active-version::after {
|
||||
content: '';
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-left: 4px solid transparent;
|
||||
border-right: 4px solid transparent;
|
||||
border-top: 6px solid #aaa;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 22px;
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.toggle-dev span,
|
||||
.toggle-ssr span {
|
||||
font-size: 12px;
|
||||
border-radius: 4px;
|
||||
padding: 4px 6px;
|
||||
}
|
||||
|
||||
.toggle-dev span {
|
||||
background: var(--purple);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.toggle-dev.dev span {
|
||||
background: var(--green);
|
||||
}
|
||||
|
||||
.toggle-ssr span {
|
||||
background-color: var(--btn-bg);
|
||||
}
|
||||
|
||||
.toggle-ssr.enabled span {
|
||||
color: #fff;
|
||||
background-color: var(--green);
|
||||
}
|
||||
|
||||
.toggle-dark svg {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
fill: #666;
|
||||
}
|
||||
|
||||
.toggle-dark .dark,
|
||||
|
@ -243,12 +289,22 @@ h1 img {
|
|||
display: inline-block;
|
||||
}
|
||||
|
||||
.version:hover .active-version:after {
|
||||
border-top-color: var(--base);
|
||||
.links button,
|
||||
.links button a {
|
||||
color: var(--btn);
|
||||
}
|
||||
|
||||
.dark .version:hover .active-version:after {
|
||||
border-top-color: #fff;
|
||||
.links button:hover,
|
||||
.links button:hover a {
|
||||
color: var(--highlight);
|
||||
}
|
||||
|
||||
.version:hover .active-version::after {
|
||||
border-top-color: var(--btn);
|
||||
}
|
||||
|
||||
.dark .version:hover .active-version::after {
|
||||
border-top-color: var(--highlight);
|
||||
}
|
||||
|
||||
.versions {
|
||||
|
@ -276,16 +332,19 @@ h1 img {
|
|||
}
|
||||
|
||||
.versions a:hover {
|
||||
color: #3ca877;
|
||||
color: var(--green);
|
||||
}
|
||||
|
||||
.versions.expanded {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.share,
|
||||
.download,
|
||||
.github {
|
||||
margin: 0 2px;
|
||||
.links > * {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.links > * + * {
|
||||
margin-left: 4px;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<svg width="1.7em" height="1.7em" viewBox="0 0 24 24">
|
||||
<g fill="#666">
|
||||
<svg width="1.7em" height="1.7em" viewBox="0 0 24 24" fill="currentColor">
|
||||
<g>
|
||||
<rect x="4" y="18" width="16" height="2" rx="1" ry="1" />
|
||||
<rect
|
||||
x="3"
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
<template>
|
||||
<svg width="1.7em" height="1.7em" viewBox="0 0 24 24">
|
||||
<svg width="1.7em" height="1.7em" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path
|
||||
fill="#666"
|
||||
d="M10.9,2.1c-4.6,0.5-8.3,4.2-8.8,8.7c-0.5,4.7,2.2,8.9,6.3,10.5C8.7,21.4,9,21.2,9,20.8v-1.6c0,0-0.4,0.1-0.9,0.1 c-1.4,0-2-1.2-2.1-1.9c-0.1-0.4-0.3-0.7-0.6-1C5.1,16.3,5,16.3,5,16.2C5,16,5.3,16,5.4,16c0.6,0,1.1,0.7,1.3,1c0.5,0.8,1.1,1,1.4,1 c0.4,0,0.7-0.1,0.9-0.2c0.1-0.7,0.4-1.4,1-1.8c-2.3-0.5-4-1.8-4-4c0-1.1,0.5-2.2,1.2-3C7.1,8.8,7,8.3,7,7.6C7,7.2,7,6.6,7.3,6 c0,0,1.4,0,2.8,1.3C10.6,7.1,11.3,7,12,7s1.4,0.1,2,0.3C15.3,6,16.8,6,16.8,6C17,6.6,17,7.2,17,7.6c0,0.8-0.1,1.2-0.2,1.4 c0.7,0.8,1.2,1.8,1.2,3c0,2.2-1.7,3.5-4,4c0.6,0.5,1,1.4,1,2.3v2.6c0,0.3,0.3,0.6,0.7,0.5c3.7-1.5,6.3-5.1,6.3-9.3 C22,6.1,16.9,1.4,10.9,2.1z"></path>
|
||||
d="M10.9,2.1c-4.6,0.5-8.3,4.2-8.8,8.7c-0.5,4.7,2.2,8.9,6.3,10.5C8.7,21.4,9,21.2,9,20.8v-1.6c0,0-0.4,0.1-0.9,0.1 c-1.4,0-2-1.2-2.1-1.9c-0.1-0.4-0.3-0.7-0.6-1C5.1,16.3,5,16.3,5,16.2C5,16,5.3,16,5.4,16c0.6,0,1.1,0.7,1.3,1c0.5,0.8,1.1,1,1.4,1 c0.4,0,0.7-0.1,0.9-0.2c0.1-0.7,0.4-1.4,1-1.8c-2.3-0.5-4-1.8-4-4c0-1.1,0.5-2.2,1.2-3C7.1,8.8,7,8.3,7,7.6C7,7.2,7,6.6,7.3,6 c0,0,1.4,0,2.8,1.3C10.6,7.1,11.3,7,12,7s1.4,0.1,2,0.3C15.3,6,16.8,6,16.8,6C17,6.6,17,7.2,17,7.6c0,0.8-0.1,1.2-0.2,1.4 c0.7,0.8,1.2,1.8,1.2,3c0,2.2-1.7,3.5-4,4c0.6,0.5,1,1.4,1,2.3v2.6c0,0.3,0.3,0.6,0.7,0.5c3.7-1.5,6.3-5.1,6.3-9.3 C22,6.1,16.9,1.4,10.9,2.1z"/>
|
||||
</svg>
|
||||
</template>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<path d="M12.1,22c-0.3,0-0.6,0-0.9,0c-5.5-0.5-9.5-5.4-9-10.9c0.4-4.8,4.2-8.6,9-9c0.4,0,0.8,0.2,1,0.5c0.2,0.3,0.2,0.8-0.1,1.1c-2,2.7-1.4,6.4,1.3,8.4c2.1,1.6,5,1.6,7.1,0c0.3-0.2,0.7-0.3,1.1-0.1c0.3,0.2,0.5,0.6,0.5,1c-0.2,2.7-1.5,5.1-3.6,6.8C16.6,21.2,14.4,22,12.1,22zM9.3,4.4c-2.9,1-5,3.6-5.2,6.8c-0.4,4.4,2.8,8.3,7.2,8.7c2.1,0.2,4.2-0.4,5.8-1.8c1.1-0.9,1.9-2.1,2.4-3.4c-2.5,0.9-5.3,0.5-7.5-1.1C9.2,11.4,8.1,7.7,9.3,4.4z" />
|
||||
<path fill="currentColor" d="M12.1,22c-0.3,0-0.6,0-0.9,0c-5.5-0.5-9.5-5.4-9-10.9c0.4-4.8,4.2-8.6,9-9c0.4,0,0.8,0.2,1,0.5c0.2,0.3,0.2,0.8-0.1,1.1c-2,2.7-1.4,6.4,1.3,8.4c2.1,1.6,5,1.6,7.1,0c0.3-0.2,0.7-0.3,1.1-0.1c0.3,0.2,0.5,0.6,0.5,1c-0.2,2.7-1.5,5.1-3.6,6.8C16.6,21.2,14.4,22,12.1,22zM9.3,4.4c-2.9,1-5,3.6-5.2,6.8c-0.4,4.4,2.8,8.3,7.2,8.7c2.1,0.2,4.2-0.4,5.8-1.8c1.1-0.9,1.9-2.1,2.4-3.4c-2.5,0.9-5.3,0.5-7.5-1.1C9.2,11.4,8.1,7.7,9.3,4.4z" />
|
||||
</svg>
|
||||
</template>
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<svg width="1.4em" height="1.4em" viewBox="0 0 24 24">
|
||||
<g
|
||||
fill="none"
|
||||
stroke="#666"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
<template>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<path d="M12,18c-3.3,0-6-2.7-6-6s2.7-6,6-6s6,2.7,6,6S15.3,18,12,18zM12,8c-2.2,0-4,1.8-4,4c0,2.2,1.8,4,4,4c2.2,0,4-1.8,4-4C16,9.8,14.2,8,12,8z" />
|
||||
<path d="M12,4c-0.6,0-1-0.4-1-1V1c0-0.6,0.4-1,1-1s1,0.4,1,1v2C13,3.6,12.6,4,12,4z" />
|
||||
<path d="M12,24c-0.6,0-1-0.4-1-1v-2c0-0.6,0.4-1,1-1s1,0.4,1,1v2C13,23.6,12.6,24,12,24z" />
|
||||
<path d="M5.6,6.6c-0.3,0-0.5-0.1-0.7-0.3L3.5,4.9c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l1.4,1.4c0.4,0.4,0.4,1,0,1.4C6.2,6.5,5.9,6.6,5.6,6.6z" />
|
||||
<path d="M19.8,20.8c-0.3,0-0.5-0.1-0.7-0.3l-1.4-1.4c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l1.4,1.4c0.4,0.4,0.4,1,0,1.4C20.3,20.7,20,20.8,19.8,20.8z" />
|
||||
<path d="M3,13H1c-0.6,0-1-0.4-1-1s0.4-1,1-1h2c0.6,0,1,0.4,1,1S3.6,13,3,13z" />
|
||||
<path d="M23,13h-2c-0.6,0-1-0.4-1-1s0.4-1,1-1h2c0.6,0,1,0.4,1,1S23.6,13,23,13z" />
|
||||
<path d="M4.2,20.8c-0.3,0-0.5-0.1-0.7-0.3c-0.4-0.4-0.4-1,0-1.4l1.4-1.4c0.4-0.4,1-0.4,1.4,0s0.4,1,0,1.4l-1.4,1.4C4.7,20.7,4.5,20.8,4.2,20.8z" />
|
||||
<path d="M18.4,6.6c-0.3,0-0.5-0.1-0.7-0.3c-0.4-0.4-0.4-1,0-1.4l1.4-1.4c0.4-0.4,1-0.4,1.4,0s0.4,1,0,1.4l-1.4,1.4C18.9,6.5,18.6,6.6,18.4,6.6z" />
|
||||
<path fill="currentColor" d="M12,18c-3.3,0-6-2.7-6-6s2.7-6,6-6s6,2.7,6,6S15.3,18,12,18zM12,8c-2.2,0-4,1.8-4,4c0,2.2,1.8,4,4,4c2.2,0,4-1.8,4-4C16,9.8,14.2,8,12,8z" />
|
||||
<path fill="currentColor" d="M12,4c-0.6,0-1-0.4-1-1V1c0-0.6,0.4-1,1-1s1,0.4,1,1v2C13,3.6,12.6,4,12,4z" />
|
||||
<path fill="currentColor" d="M12,24c-0.6,0-1-0.4-1-1v-2c0-0.6,0.4-1,1-1s1,0.4,1,1v2C13,23.6,12.6,24,12,24z" />
|
||||
<path fill="currentColor" d="M5.6,6.6c-0.3,0-0.5-0.1-0.7-0.3L3.5,4.9c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l1.4,1.4c0.4,0.4,0.4,1,0,1.4C6.2,6.5,5.9,6.6,5.6,6.6z" />
|
||||
<path fill="currentColor" d="M19.8,20.8c-0.3,0-0.5-0.1-0.7-0.3l-1.4-1.4c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l1.4,1.4c0.4,0.4,0.4,1,0,1.4C20.3,20.7,20,20.8,19.8,20.8z" />
|
||||
<path fill="currentColor" d="M3,13H1c-0.6,0-1-0.4-1-1s0.4-1,1-1h2c0.6,0,1,0.4,1,1S3.6,13,3,13z" />
|
||||
<path fill="currentColor" d="M23,13h-2c-0.6,0-1-0.4-1-1s0.4-1,1-1h2c0.6,0,1,0.4,1,1S23.6,13,23,13z" />
|
||||
<path fill="currentColor" d="M4.2,20.8c-0.3,0-0.5-0.1-0.7-0.3c-0.4-0.4-0.4-1,0-1.4l1.4-1.4c0.4-0.4,1-0.4,1.4,0s0.4,1,0,1.4l-1.4,1.4C4.7,20.7,4.5,20.8,4.2,20.8z" />
|
||||
<path fill="currentColor" d="M18.4,6.6c-0.3,0-0.5-0.1-0.7-0.3c-0.4-0.4-0.4-1,0-1.4l1.4-1.4c0.4-0.4,1-0.4,1.4,0s0.4,1,0,1.4l-1.4,1.4C18.9,6.5,18.6,6.6,18.4,6.6z" />
|
||||
</svg>
|
||||
</template>
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
// serve vue/server-renderer to the iframe sandbox during dev.
|
||||
export * from 'vue/server-renderer'
|
|
@ -21,21 +21,24 @@ function copyVuePlugin(): Plugin {
|
|||
return {
|
||||
name: 'copy-vue',
|
||||
generateBundle() {
|
||||
const filePath = path.resolve(
|
||||
__dirname,
|
||||
'../vue/dist/vue.runtime.esm-browser.js'
|
||||
)
|
||||
if (!fs.existsSync(filePath)) {
|
||||
throw new Error(
|
||||
`vue.runtime.esm-browser.js not built. ` +
|
||||
`Run "nr build vue -f esm-browser" first.`
|
||||
)
|
||||
const copyFile = (file: string) => {
|
||||
const filePath = path.resolve(__dirname, file)
|
||||
const basename = path.basename(file)
|
||||
if (!fs.existsSync(filePath)) {
|
||||
throw new Error(
|
||||
`${basename} not built. ` +
|
||||
`Run "nr build vue -f esm-browser" first.`
|
||||
)
|
||||
}
|
||||
this.emitFile({
|
||||
type: 'asset',
|
||||
fileName: basename,
|
||||
source: fs.readFileSync(filePath, 'utf-8')
|
||||
})
|
||||
}
|
||||
this.emitFile({
|
||||
type: 'asset',
|
||||
fileName: 'vue.runtime.esm-browser.js',
|
||||
source: fs.readFileSync(filePath, 'utf-8')
|
||||
})
|
||||
|
||||
copyFile(`../vue/dist/vue.runtime.esm-browser.js`)
|
||||
copyFile(`../server-renderer/dist/server-renderer.esm-browser.js`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@vue/shared",
|
||||
"version": "3.2.33",
|
||||
"version": "3.2.37",
|
||||
"description": "internal utils shared across @vue packages",
|
||||
"main": "index.js",
|
||||
"module": "dist/shared.esm-bundler.js",
|
||||
|
|
|
@ -52,7 +52,7 @@ export const isMap = (val: unknown): val is Map<any, any> =>
|
|||
export const isSet = (val: unknown): val is Set<any> =>
|
||||
toTypeString(val) === '[object Set]'
|
||||
|
||||
export const isDate = (val: unknown): val is Date => val instanceof Date
|
||||
export const isDate = (val: unknown): val is Date => toTypeString(val) === '[object Date]'
|
||||
export const isFunction = (val: unknown): val is Function =>
|
||||
typeof val === 'function'
|
||||
export const isString = (val: unknown): val is string => typeof val === 'string'
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@vue/size-check",
|
||||
"version": "3.2.33",
|
||||
"version": "3.2.37",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"build": "vite build"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@vue/template-explorer",
|
||||
"version": "3.2.33",
|
||||
"version": "3.2.37",
|
||||
"private": true,
|
||||
"buildOptions": {
|
||||
"formats": [
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import Vue from '@vue/compat'
|
||||
import { effect, isReactive } from '@vue/reactivity'
|
||||
import { nextTick } from '@vue/runtime-core'
|
||||
import { h, nextTick } from '@vue/runtime-core'
|
||||
import {
|
||||
DeprecationTypes,
|
||||
deprecationData,
|
||||
|
@ -448,3 +448,57 @@ test('global asset registration should affect apps created via createApp', () =>
|
|||
expect(vm.$el.textContent).toBe('foo')
|
||||
delete singletonApp._context.components.foo
|
||||
})
|
||||
|
||||
test('post-facto global asset registration should affect apps created via createApp', () => {
|
||||
const app = createApp({
|
||||
template: '<foo/>'
|
||||
})
|
||||
Vue.component('foo', { template: 'foo' })
|
||||
const vm = app.mount(document.createElement('div')) as any
|
||||
expect(vm.$el.textContent).toBe('foo')
|
||||
delete singletonApp._context.components.foo
|
||||
})
|
||||
|
||||
test('local asset registration should not affect other local apps', () => {
|
||||
const app1 = createApp({})
|
||||
const app2 = createApp({})
|
||||
|
||||
app1.component('foo', {})
|
||||
app2.component('foo', {})
|
||||
|
||||
expect(
|
||||
`Component "foo" has already been registered in target app`
|
||||
).not.toHaveBeenWarned()
|
||||
})
|
||||
|
||||
test('local app-level mixin registration should not affect other local apps', () => {
|
||||
const app1 = createApp({ render: () => h('div') })
|
||||
const app2 = createApp({})
|
||||
|
||||
const mixin = { created: jest.fn() }
|
||||
app1.mixin(mixin)
|
||||
app2.mixin(mixin)
|
||||
|
||||
expect(`Mixin has already been applied`).not.toHaveBeenWarned()
|
||||
|
||||
app1.mount(document.createElement('div'))
|
||||
expect(mixin.created).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
|
||||
// #5699
|
||||
test('local app config should not affect other local apps in v3 mode', () => {
|
||||
Vue.configureCompat({ MODE: 3 })
|
||||
const app1 = createApp({
|
||||
render: () => h('div'),
|
||||
provide() {
|
||||
return {
|
||||
test: 123
|
||||
}
|
||||
}
|
||||
})
|
||||
app1.config.globalProperties.test = () => {}
|
||||
app1.mount(document.createElement('div'))
|
||||
|
||||
const app2 = createApp({})
|
||||
expect(app2.config.globalProperties.test).toBe(undefined)
|
||||
})
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@vue/compat",
|
||||
"version": "3.2.33",
|
||||
"version": "3.2.37",
|
||||
"description": "Vue 3 compatibility build for Vue 2",
|
||||
"main": "index.js",
|
||||
"module": "dist/vue.runtime.esm-bundler.js",
|
||||
|
@ -38,6 +38,6 @@
|
|||
},
|
||||
"homepage": "https://github.com/vuejs/core/tree/main/packages/vue-compat#readme",
|
||||
"peerDependencies": {
|
||||
"vue": "3.2.33"
|
||||
"vue": "3.2.37"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "vue",
|
||||
"version": "3.2.33",
|
||||
"version": "3.2.37",
|
||||
"description": "The progressive JavaScript framework for building modern web UI.",
|
||||
"main": "index.js",
|
||||
"module": "dist/vue.runtime.esm-bundler.js",
|
||||
|
@ -66,10 +66,10 @@
|
|||
},
|
||||
"homepage": "https://github.com/vuejs/core/tree/main/packages/vue#readme",
|
||||
"dependencies": {
|
||||
"@vue/shared": "3.2.33",
|
||||
"@vue/compiler-dom": "3.2.33",
|
||||
"@vue/runtime-dom": "3.2.33",
|
||||
"@vue/compiler-sfc": "3.2.33",
|
||||
"@vue/server-renderer": "3.2.33"
|
||||
"@vue/shared": "3.2.37",
|
||||
"@vue/compiler-dom": "3.2.37",
|
||||
"@vue/runtime-dom": "3.2.37",
|
||||
"@vue/compiler-sfc": "3.2.37",
|
||||
"@vue/server-renderer": "3.2.37"
|
||||
}
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue