-
-
- +
-
- +- +
diff --git a/.eslintrc.js b/.eslintrc.js
deleted file mode 100644
index ea44a0001..000000000
--- a/.eslintrc.js
+++ /dev/null
@@ -1,69 +0,0 @@
-const DOMGlobals = ['window', 'document']
-const NodeGlobals = ['module', 'require']
-
-module.exports = {
- parser: '@typescript-eslint/parser',
- parserOptions: {
- sourceType: 'module'
- },
- rules: {
- 'no-unused-vars': [
- 'error',
- // we are only using this rule to check for unused arguments since TS
- // catches unused variables but not args.
- { varsIgnorePattern: '.*', args: 'none' }
- ],
- // most of the codebase are expected to be env agnostic
- 'no-restricted-globals': ['error', ...DOMGlobals, ...NodeGlobals],
- // since we target ES2015 for baseline support, we need to forbid object
- // rest spread usage (both assign and destructure)
- 'no-restricted-syntax': [
- 'error',
- 'ObjectExpression > SpreadElement',
- 'ObjectPattern > RestElement',
- 'AwaitExpression'
- ]
- },
- overrides: [
- // tests, no restrictions (runs in Node / jest with jsdom)
- {
- files: ['**/__tests__/**', 'test-dts/**'],
- rules: {
- 'no-restricted-globals': 'off',
- 'no-restricted-syntax': 'off'
- }
- },
- // shared, may be used in any env
- {
- files: ['packages/shared/**'],
- rules: {
- 'no-restricted-globals': 'off'
- }
- },
- // Packages targeting DOM
- {
- files: ['packages/{vue,vue-compat,runtime-dom}/**'],
- rules: {
- 'no-restricted-globals': ['error', ...NodeGlobals]
- }
- },
- // Packages targeting Node
- {
- files: [
- 'packages/{compiler-sfc,compiler-ssr,server-renderer,ref-transform}/**'
- ],
- rules: {
- 'no-restricted-globals': ['error', ...DOMGlobals],
- 'no-restricted-syntax': 'off'
- }
- },
- // Private package, browser only + no syntax restrictions
- {
- files: ['packages/template-explorer/**', 'packages/sfc-playground/**'],
- rules: {
- 'no-restricted-globals': ['error', ...NodeGlobals],
- 'no-restricted-syntax': 'off'
- }
- }
- ]
-}
diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs
new file mode 100644
index 000000000..d06b03a3f
--- /dev/null
+++ b/.git-blame-ignore-revs
@@ -0,0 +1,2 @@
+# update prettier & eslint config (#9162)
+bfe6b459d3a0ce6168611ee1ac7e6e789709df9d
diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
index 4a8f6fd42..9288efdb9 100644
--- a/.github/FUNDING.yml
+++ b/.github/FUNDING.yml
@@ -1,3 +1,2 @@
-open_collective: vuejs
-patreon: evanyou
github: yyx990803
+open_collective: vuejs
diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml
new file mode 100644
index 000000000..95e0ca79c
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/bug_report.yml
@@ -0,0 +1,74 @@
+name: "\U0001F41E Bug report"
+description: Create a report to help us improve
+body:
+ - type: markdown
+ attributes:
+ value: |
+ **Before You Start...**
+
+ This form is only for submitting bug reports. If you have a usage question
+ or are unsure if this is really a bug, make sure to:
+
+ - Read the [docs](https://vuejs.org/)
+ - Ask on [Discord Chat](https://chat.vuejs.org/)
+ - Ask on [GitHub Discussions](https://github.com/vuejs/core/discussions)
+ - Look for / ask questions on [Stack Overflow](https://stackoverflow.com/questions/ask?tags=vue.js)
+
+ 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:
+ label: Link to minimal reproduction
+ description: |
+ The easiest way to provide a reproduction is by showing the bug in [The SFC Playground](https://play.vuejs.org/).
+ If it cannot be reproduced in the playground and requires a proper build setup, try [StackBlitz](https://vite.new/vue).
+ If neither of these are suitable, you can always provide a GitHub repository.
+
+ The reproduction should be **minimal** - i.e. it should contain only the bare minimum amount of code needed
+ to show the bug. See [Bug Reproduction Guidelines](https://github.com/vuejs/core/blob/main/.github/bug-repro-guidelines.md) for more details.
+
+ Please do not just fill in a random link. The issue will be closed if no valid reproduction is provided.
+ placeholder: Reproduction Link
+ validations:
+ required: true
+ - type: textarea
+ id: steps-to-reproduce
+ attributes:
+ label: Steps to reproduce
+ description: |
+ What do we need to do after opening your repro in order to make the bug happen? Clear and concise reproduction instructions are important for us to be able to triage your issue in a timely manner. Note that you can use [Markdown](https://guides.github.com/features/mastering-markdown/) to format lists and code.
+ placeholder: Steps to reproduce
+ validations:
+ required: true
+ - type: textarea
+ id: expected
+ attributes:
+ label: What is expected?
+ validations:
+ required: true
+ - type: textarea
+ id: actually-happening
+ attributes:
+ label: What is actually happening?
+ validations:
+ required: true
+ - type: textarea
+ id: system-info
+ attributes:
+ label: System Info
+ description: Output of `npx envinfo --system --npmPackages vue --binaries --browsers`
+ render: shell
+ placeholder: System, Binaries, Browsers
+ - type: textarea
+ id: additional-comments
+ attributes:
+ label: Any additional comments?
+ description: e.g. some background/context of how you ran into this bug.
diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml
index ac8a00ef1..02f99c6bf 100644
--- a/.github/ISSUE_TEMPLATE/config.yml
+++ b/.github/ISSUE_TEMPLATE/config.yml
@@ -1,8 +1,14 @@
blank_issues_enabled: false
contact_links:
- - name: Create new issue
- url: https://new-issue.vuejs.org/?repo=vuejs/vue-next
- about: Please use the following link to create a new issue.
+ - name: Feature Request
+ url: https://github.com/vuejs/rfcs/discussions
+ about: Suggest new features for consideration
+ - name: Discord Chat
+ url: https://chat.vuejs.org
+ about: Ask questions and discuss with other Vue users in real time.
+ - name: Questions & Discussions
+ url: https://github.com/vuejs/core/discussions
+ about: Use GitHub discussions for message-board style questions and discussions.
- name: Patreon
url: https://www.patreon.com/evanyou
about: Love Vue.js? Please consider supporting us via Patreon.
diff --git a/.github/bug-repro-guidelines.md b/.github/bug-repro-guidelines.md
new file mode 100644
index 000000000..90458b307
--- /dev/null
+++ b/.github/bug-repro-guidelines.md
@@ -0,0 +1,29 @@
+## About Bug Reproductions
+
+A bug reproduction is a piece of code that can run and demonstrate how a bug can happen.
+
+### Text is not enough
+
+It's impossible to fix a bug from mere text descriptions. First, it's very difficult to precisely describe a technical problem while keeping it easy to follow; Second, the real cause may very well be something that you forgot to even mention. A reproduction is the only way that can reliably help us understand what is going on, so please provide one.
+
+### A repro must be runnable
+
+Screenshots or videos are NOT reproductions! They only show that the bug exists, but do not provide enough information on why it happens. Only runnable code provides the most complete context and allows us to properly debug the scenario. That said, in some cases videos/gifs can help explain interaction issues that are hard to describe in text.
+
+### A repro should be minimal
+
+Some users would give us a link to a real project and hope we can help them figure out what is wrong. We generally do not accept such requests because:
+
+You are already familiar with your codebase, but we are not. It is extremely time-consuming to hunt a bug in a big and unfamiliar codebase.
+
+The problematic behavior may very well be caused by your code rather than by a bug in Vue.
+
+A minimal reproduction means it demonstrates the bug, and the bug only. It should only contain the bare minimum amount of code that can reliably cause the bug. Try your best to get rid of anything that aren't directly related to the problem.
+
+### How to create a repro
+
+For Vue 3 core reproductions, try reproducing it in [The SFC Playground](https://play.vuejs.org/).
+
+If it cannot be reproduced in the playground and requires a proper build setup, try [StackBlitz](https://vite.new/vue).
+
+If neither of these are suitable, you can always provide a GitHub repository.
diff --git a/.github/commit-convention.md b/.github/commit-convention.md
index a8522fa21..11a64576a 100644
--- a/.github/commit-convention.md
+++ b/.github/commit-convention.md
@@ -6,7 +6,7 @@
Messages must be matched by the following regex:
-``` js
+```regexp
/^(revert: )?(feat|fix|docs|dx|style|refactor|perf|test|workflow|build|ci|chore|types|wip)(\(.+\))?: .{1,50}/
```
@@ -44,7 +44,7 @@ This reverts commit 667ecc1654a317a13331b17617d973392f415f02.
### Full Message Format
-A commit message consists of a **header**, **body** and **footer**. The header has a **type**, **scope** and **subject**:
+A commit message consists of a **header**, **body** and **footer**. The header has a **type**, **scope** and **subject**:
```
+
+
+
+
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
deleted file mode 100644
index af713d27a..000000000
--- a/.github/dependabot.yml
+++ /dev/null
@@ -1,64 +0,0 @@
-version: 2
-updates:
-- package-ecosystem: npm
- directory: "/"
- schedule:
- interval: monthly
- open-pull-requests-limit: 10
- versioning-strategy: lockfile-only
- ignore:
- - dependency-name: "@types/node"
- versions:
- - 14.14.24
- - 14.14.37
- - dependency-name: "@babel/parser"
- versions:
- - 7.12.11
- - 7.12.13
- - 7.12.14
- - 7.12.15
- - 7.12.16
- - 7.12.17
- - 7.13.0
- - 7.13.10
- - 7.13.11
- - 7.13.13
- - 7.13.4
- - 7.13.9
- - dependency-name: eslint
- versions:
- - 7.23.0
- - dependency-name: postcss
- versions:
- - 8.2.4
- - 8.2.5
- - 8.2.7
- - 8.2.8
- - dependency-name: typescript
- versions:
- - 4.2.2
- - dependency-name: "@babel/types"
- versions:
- - 7.12.12
- - 7.12.13
- - 7.12.17
- - 7.13.0
- - dependency-name: pug-code-gen
- versions:
- - 2.0.3
- - dependency-name: estree-walker
- versions:
- - 2.0.2
- - dependency-name: "@typescript-eslint/parser"
- versions:
- - 4.14.2
- - 4.15.0
- - dependency-name: "@microsoft/api-extractor"
- versions:
- - 7.13.1
- - dependency-name: rollup
- versions:
- - 2.38.5
- - dependency-name: node-notifier
- versions:
- - 8.0.1
diff --git a/.github/git-branch-workflow.excalidraw b/.github/git-branch-workflow.excalidraw
new file mode 100644
index 000000000..dd9127938
--- /dev/null
+++ b/.github/git-branch-workflow.excalidraw
@@ -0,0 +1,1746 @@
+{
+ "type": "excalidraw",
+ "version": 2,
+ "source": "https://excalidraw.com",
+ "elements": [
+ {
+ "type": "arrow",
+ "version": 799,
+ "versionNonce": 529220601,
+ "isDeleted": false,
+ "id": "Gao2krnDddLMCj468JSWD",
+ "fillStyle": "solid",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 860.0129225738813,
+ "y": 663.9911710635109,
+ "strokeColor": "#f08c00",
+ "backgroundColor": "#ffc9c9",
+ "width": 133.75296854079784,
+ "height": 149.58016791936518,
+ "seed": 1415631543,
+ "groupIds": [],
+ "frameId": null,
+ "roundness": {
+ "type": 2
+ },
+ "boundElements": [],
+ "updated": 1698927613071,
+ "link": null,
+ "locked": false,
+ "startBinding": null,
+ "endBinding": {
+ "elementId": "hDC6an14QljktaZCUhcPF",
+ "focus": 0.09950793234484598,
+ "gap": 1.2432497743127229
+ },
+ "lastCommittedPoint": null,
+ "startArrowhead": null,
+ "endArrowhead": "arrow",
+ "points": [
+ [
+ 0,
+ 0
+ ],
+ [
+ 25.209039386719837,
+ 85.96948921803892
+ ],
+ [
+ 133.75296854079784,
+ 149.58016791936518
+ ]
+ ]
+ },
+ {
+ "type": "arrow",
+ "version": 563,
+ "versionNonce": 290881303,
+ "isDeleted": false,
+ "id": "N3wyyEU7TQ8BsOQgxCmlR",
+ "fillStyle": "hachure",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 2,
+ "opacity": 100,
+ "angle": 0,
+ "x": 292.88008929085873,
+ "y": 660.7027503334302,
+ "strokeColor": "#2f9e44",
+ "backgroundColor": "#b2f2bb",
+ "width": 936.9972134376155,
+ "height": 1.3184243543457796,
+ "seed": 534235417,
+ "groupIds": [],
+ "frameId": null,
+ "roundness": {
+ "type": 2
+ },
+ "boundElements": [],
+ "updated": 1698927613071,
+ "link": null,
+ "locked": false,
+ "startBinding": null,
+ "endBinding": null,
+ "lastCommittedPoint": null,
+ "startArrowhead": null,
+ "endArrowhead": "arrow",
+ "points": [
+ [
+ 0,
+ 0
+ ],
+ [
+ 936.9972134376155,
+ -1.3184243543457796
+ ]
+ ]
+ },
+ {
+ "type": "arrow",
+ "version": 302,
+ "versionNonce": 883286489,
+ "isDeleted": false,
+ "id": "nRDWQs5nQa37yzCWTBiXC",
+ "fillStyle": "hachure",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 2,
+ "opacity": 100,
+ "angle": 0,
+ "x": 293.1231624544633,
+ "y": 820.6017661012943,
+ "strokeColor": "#f08c00",
+ "backgroundColor": "#b2f2bb",
+ "width": 790.7091601354882,
+ "height": 0.35284814071621895,
+ "seed": 515907671,
+ "groupIds": [],
+ "frameId": null,
+ "roundness": {
+ "type": 2
+ },
+ "boundElements": [],
+ "updated": 1698927613071,
+ "link": null,
+ "locked": false,
+ "startBinding": null,
+ "endBinding": {
+ "elementId": "ggogfJT7E_bbfEog7Hjnp",
+ "focus": -0.14000162237652433,
+ "gap": 1
+ },
+ "lastCommittedPoint": null,
+ "startArrowhead": null,
+ "endArrowhead": "arrow",
+ "points": [
+ [
+ 0,
+ 0
+ ],
+ [
+ 790.7091601354882,
+ -0.35284814071621895
+ ]
+ ]
+ },
+ {
+ "type": "text",
+ "version": 36,
+ "versionNonce": 981763127,
+ "isDeleted": false,
+ "id": "ZPdMAnEUq5Jgj1W07Zqiw",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 2,
+ "opacity": 100,
+ "angle": 0,
+ "x": 292.0450153578305,
+ "y": 619.3959946602608,
+ "strokeColor": "#1e1e1e",
+ "backgroundColor": "#b2f2bb",
+ "width": 46.875,
+ "height": 24,
+ "seed": 1311694519,
+ "groupIds": [],
+ "frameId": null,
+ "roundness": null,
+ "boundElements": [],
+ "updated": 1698927613071,
+ "link": null,
+ "locked": false,
+ "fontSize": 20,
+ "fontFamily": 3,
+ "text": "main",
+ "textAlign": "left",
+ "verticalAlign": "top",
+ "containerId": null,
+ "originalText": "main",
+ "lineHeight": 1.2,
+ "baseline": 20
+ },
+ {
+ "type": "text",
+ "version": 94,
+ "versionNonce": 18759353,
+ "isDeleted": false,
+ "id": "g9IkEIfu4vA8Qkwtw01Hi",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 2,
+ "opacity": 100,
+ "angle": 0,
+ "x": 290.88990199912035,
+ "y": 779.1760596323645,
+ "strokeColor": "#1e1e1e",
+ "backgroundColor": "#b2f2bb",
+ "width": 58.59375,
+ "height": 24,
+ "seed": 329886135,
+ "groupIds": [],
+ "frameId": null,
+ "roundness": null,
+ "boundElements": [],
+ "updated": 1698927613071,
+ "link": null,
+ "locked": false,
+ "fontSize": 20,
+ "fontFamily": 3,
+ "text": "minor",
+ "textAlign": "left",
+ "verticalAlign": "top",
+ "containerId": null,
+ "originalText": "minor",
+ "lineHeight": 1.2,
+ "baseline": 20
+ },
+ {
+ "type": "ellipse",
+ "version": 50,
+ "versionNonce": 1442112855,
+ "isDeleted": false,
+ "id": "RrdEQ7hwgGGDPhzDnuZj1",
+ "fillStyle": "solid",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 2,
+ "opacity": 100,
+ "angle": 0,
+ "x": 361.55609907891005,
+ "y": 649.8742329483416,
+ "strokeColor": "#2f9e44",
+ "backgroundColor": "#b2f2bb",
+ "width": 18.814646969963974,
+ "height": 18.814646969963974,
+ "seed": 2077639991,
+ "groupIds": [],
+ "frameId": null,
+ "roundness": {
+ "type": 2
+ },
+ "boundElements": [],
+ "updated": 1698927613071,
+ "link": null,
+ "locked": false
+ },
+ {
+ "type": "ellipse",
+ "version": 79,
+ "versionNonce": 1547173785,
+ "isDeleted": false,
+ "id": "Zmp49FKWxGSzKnVKomjQc",
+ "fillStyle": "solid",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 2,
+ "opacity": 100,
+ "angle": 0,
+ "x": 427.3015090315691,
+ "y": 650.256485100784,
+ "strokeColor": "#2f9e44",
+ "backgroundColor": "#b2f2bb",
+ "width": 18.814646969963974,
+ "height": 18.814646969963974,
+ "seed": 372652121,
+ "groupIds": [],
+ "frameId": null,
+ "roundness": {
+ "type": 2
+ },
+ "boundElements": [],
+ "updated": 1698927613071,
+ "link": null,
+ "locked": false
+ },
+ {
+ "type": "ellipse",
+ "version": 76,
+ "versionNonce": 586949239,
+ "isDeleted": false,
+ "id": "UOl9nLBksM7RPdH9mzjJa",
+ "fillStyle": "solid",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 2,
+ "opacity": 100,
+ "angle": 0,
+ "x": 490.9435520120701,
+ "y": 651.2601420343765,
+ "strokeColor": "#2f9e44",
+ "backgroundColor": "#b2f2bb",
+ "width": 18.814646969963974,
+ "height": 18.814646969963974,
+ "seed": 508667545,
+ "groupIds": [],
+ "frameId": null,
+ "roundness": {
+ "type": 2
+ },
+ "boundElements": [],
+ "updated": 1698927613071,
+ "link": null,
+ "locked": false
+ },
+ {
+ "type": "ellipse",
+ "version": 120,
+ "versionNonce": 874947705,
+ "isDeleted": false,
+ "id": "oMC55V0VO_hOXoZ1se8Kl",
+ "fillStyle": "solid",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 2,
+ "opacity": 100,
+ "angle": 0,
+ "x": 555.4481126698772,
+ "y": 650.7975189165487,
+ "strokeColor": "#2f9e44",
+ "backgroundColor": "#b2f2bb",
+ "width": 18.814646969963974,
+ "height": 18.814646969963974,
+ "seed": 1914963513,
+ "groupIds": [],
+ "frameId": null,
+ "roundness": {
+ "type": 2
+ },
+ "boundElements": [],
+ "updated": 1698927613071,
+ "link": null,
+ "locked": false
+ },
+ {
+ "type": "ellipse",
+ "version": 66,
+ "versionNonce": 39762839,
+ "isDeleted": false,
+ "id": "DZY5DC5uVP7-U5c3ngIZ4",
+ "fillStyle": "solid",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 2,
+ "opacity": 100,
+ "angle": 0,
+ "x": 622.5167031502219,
+ "y": 649.3743647489936,
+ "strokeColor": "#2f9e44",
+ "backgroundColor": "#b2f2bb",
+ "width": 18.814646969963974,
+ "height": 18.814646969963974,
+ "seed": 165914713,
+ "groupIds": [],
+ "frameId": null,
+ "roundness": {
+ "type": 2
+ },
+ "boundElements": [],
+ "updated": 1698927613071,
+ "link": null,
+ "locked": false
+ },
+ {
+ "type": "ellipse",
+ "version": 107,
+ "versionNonce": 1689103705,
+ "isDeleted": false,
+ "id": "Vsw6oIiTM3fQypkiCic3f",
+ "fillStyle": "solid",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 2,
+ "opacity": 100,
+ "angle": 0,
+ "x": 690.330195260967,
+ "y": 650.6681412649529,
+ "strokeColor": "#2f9e44",
+ "backgroundColor": "#b2f2bb",
+ "width": 18.814646969963974,
+ "height": 18.814646969963974,
+ "seed": 280044345,
+ "groupIds": [],
+ "frameId": null,
+ "roundness": {
+ "type": 2
+ },
+ "boundElements": [
+ {
+ "id": "lwYvAs-7FTjcwxKjcx0KV",
+ "type": "arrow"
+ }
+ ],
+ "updated": 1698927613071,
+ "link": null,
+ "locked": false
+ },
+ {
+ "type": "ellipse",
+ "version": 148,
+ "versionNonce": 1986194201,
+ "isDeleted": false,
+ "id": "D14w9erv_2l53mINe2nSt",
+ "fillStyle": "solid",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 2,
+ "opacity": 100,
+ "angle": 0,
+ "x": 361.004283792179,
+ "y": 810.2809579853473,
+ "strokeColor": "#f08c00",
+ "backgroundColor": "#ffc9c9",
+ "width": 18.814646969963974,
+ "height": 18.814646969963974,
+ "seed": 1203257975,
+ "groupIds": [],
+ "frameId": null,
+ "roundness": {
+ "type": 2
+ },
+ "boundElements": [],
+ "updated": 1698927613071,
+ "link": null,
+ "locked": false
+ },
+ {
+ "type": "ellipse",
+ "version": 179,
+ "versionNonce": 1172811511,
+ "isDeleted": false,
+ "id": "6WO8xOpG0rf673b_bT0m7",
+ "fillStyle": "solid",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 2,
+ "opacity": 100,
+ "angle": 0,
+ "x": 426.74969374483805,
+ "y": 810.6632101377896,
+ "strokeColor": "#f08c00",
+ "backgroundColor": "#b2f2bb",
+ "width": 18.814646969963974,
+ "height": 18.814646969963974,
+ "seed": 2056706967,
+ "groupIds": [],
+ "frameId": null,
+ "roundness": {
+ "type": 2
+ },
+ "boundElements": [
+ {
+ "id": "mE8Mu0qKfFaWPCC5vmF_f",
+ "type": "arrow"
+ }
+ ],
+ "updated": 1698927613071,
+ "link": null,
+ "locked": false
+ },
+ {
+ "type": "ellipse",
+ "version": 173,
+ "versionNonce": 820518905,
+ "isDeleted": false,
+ "id": "VB9U8oH-78hf530hIb_mG",
+ "fillStyle": "solid",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 2,
+ "opacity": 100,
+ "angle": 0,
+ "x": 490.391736725339,
+ "y": 811.6668670713822,
+ "strokeColor": "#f08c00",
+ "backgroundColor": "#b2f2bb",
+ "width": 18.814646969963974,
+ "height": 18.814646969963974,
+ "seed": 1149587639,
+ "groupIds": [],
+ "frameId": null,
+ "roundness": {
+ "type": 2
+ },
+ "boundElements": [],
+ "updated": 1698927613071,
+ "link": null,
+ "locked": false
+ },
+ {
+ "type": "ellipse",
+ "version": 218,
+ "versionNonce": 1227143191,
+ "isDeleted": false,
+ "id": "Bxv1hcS0VmxUwI0JLFH97",
+ "fillStyle": "solid",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 2,
+ "opacity": 100,
+ "angle": 0,
+ "x": 554.8962973831461,
+ "y": 811.2042439535543,
+ "strokeColor": "#f08c00",
+ "backgroundColor": "#b2f2bb",
+ "width": 18.814646969963974,
+ "height": 18.814646969963974,
+ "seed": 1864901079,
+ "groupIds": [],
+ "frameId": null,
+ "roundness": {
+ "type": 2
+ },
+ "boundElements": [
+ {
+ "id": "M14Q0Uo1DBy2Ss2SOFSgW",
+ "type": "arrow"
+ }
+ ],
+ "updated": 1698927613071,
+ "link": null,
+ "locked": false
+ },
+ {
+ "type": "ellipse",
+ "version": 167,
+ "versionNonce": 1387509977,
+ "isDeleted": false,
+ "id": "4v23gkfhy-hzk18YdkfLz",
+ "fillStyle": "solid",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 2,
+ "opacity": 100,
+ "angle": 0,
+ "x": 621.9648878634908,
+ "y": 809.7810897859994,
+ "strokeColor": "#f08c00",
+ "backgroundColor": "#ffc9c9",
+ "width": 18.814646969963974,
+ "height": 18.814646969963974,
+ "seed": 462671607,
+ "groupIds": [],
+ "frameId": null,
+ "roundness": {
+ "type": 2
+ },
+ "boundElements": [
+ {
+ "id": "vEF1cIIYYWKm84KLKqEz3",
+ "type": "arrow"
+ }
+ ],
+ "updated": 1698927613071,
+ "link": null,
+ "locked": false
+ },
+ {
+ "type": "ellipse",
+ "version": 200,
+ "versionNonce": 774085943,
+ "isDeleted": false,
+ "id": "AtEf7o4WZQn4Zxq8EN5fH",
+ "fillStyle": "solid",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 2,
+ "opacity": 100,
+ "angle": 0,
+ "x": 689.7783799742359,
+ "y": 811.0748663019584,
+ "strokeColor": "#f08c00",
+ "backgroundColor": "#b2f2bb",
+ "width": 18.814646969963974,
+ "height": 18.814646969963974,
+ "seed": 1414322199,
+ "groupIds": [],
+ "frameId": null,
+ "roundness": {
+ "type": 2
+ },
+ "boundElements": [
+ {
+ "id": "3heKY3vfe3-6ni4dX7Uqo",
+ "type": "arrow"
+ }
+ ],
+ "updated": 1698927613071,
+ "link": null,
+ "locked": false
+ },
+ {
+ "type": "ellipse",
+ "version": 199,
+ "versionNonce": 1834563001,
+ "isDeleted": false,
+ "id": "ugDby5sBv4NKdNt8eC1sg",
+ "fillStyle": "solid",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 2,
+ "opacity": 100,
+ "angle": 0,
+ "x": 762.6179978227377,
+ "y": 810.2986003923828,
+ "strokeColor": "#f08c00",
+ "backgroundColor": "#b2f2bb",
+ "width": 18.814646969963974,
+ "height": 18.814646969963974,
+ "seed": 1598537015,
+ "groupIds": [],
+ "frameId": null,
+ "roundness": {
+ "type": 2
+ },
+ "boundElements": [],
+ "updated": 1698927613071,
+ "link": null,
+ "locked": false
+ },
+ {
+ "type": "ellipse",
+ "version": 211,
+ "versionNonce": 407428695,
+ "isDeleted": false,
+ "id": "Fwe4F2sB_0jptOZGYsusj",
+ "fillStyle": "solid",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 2,
+ "opacity": 100,
+ "angle": 0,
+ "x": 837.1081608628116,
+ "y": 810.859236882632,
+ "strokeColor": "#f08c00",
+ "backgroundColor": "#b2f2bb",
+ "width": 18.814646969963974,
+ "height": 18.814646969963974,
+ "seed": 1340669527,
+ "groupIds": [],
+ "frameId": null,
+ "roundness": {
+ "type": 2
+ },
+ "boundElements": [
+ {
+ "id": "M14Q0Uo1DBy2Ss2SOFSgW",
+ "type": "arrow"
+ }
+ ],
+ "updated": 1698927613071,
+ "link": null,
+ "locked": false
+ },
+ {
+ "type": "arrow",
+ "version": 57,
+ "versionNonce": 335287961,
+ "isDeleted": false,
+ "id": "mE8Mu0qKfFaWPCC5vmF_f",
+ "fillStyle": "solid",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 2,
+ "opacity": 100,
+ "angle": 0,
+ "x": 437.60867586595543,
+ "y": 830.4227236701945,
+ "strokeColor": "#f08c00",
+ "backgroundColor": "#ffc9c9",
+ "width": 0.5232394659406623,
+ "height": 33.25787987764363,
+ "seed": 482155929,
+ "groupIds": [],
+ "frameId": null,
+ "roundness": {
+ "type": 2
+ },
+ "boundElements": [],
+ "updated": 1698927613071,
+ "link": null,
+ "locked": false,
+ "startBinding": {
+ "elementId": "6WO8xOpG0rf673b_bT0m7",
+ "focus": -0.1727591064041787,
+ "gap": 1.046152088903881
+ },
+ "endBinding": {
+ "elementId": "JALHBtowuh3_a86loej2x",
+ "focus": 0.015156451076917701,
+ "gap": 15.586906139714472
+ },
+ "lastCommittedPoint": null,
+ "startArrowhead": null,
+ "endArrowhead": "arrow",
+ "points": [
+ [
+ 0,
+ 0
+ ],
+ [
+ -0.5232394659406623,
+ 33.25787987764363
+ ]
+ ]
+ },
+ {
+ "type": "arrow",
+ "version": 59,
+ "versionNonce": 1248394103,
+ "isDeleted": false,
+ "id": "AI-_jSAuzesxTqwRvpk0s",
+ "fillStyle": "solid",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 2,
+ "opacity": 100,
+ "angle": 0,
+ "x": 501.2878833373983,
+ "y": 652.3088851192829,
+ "strokeColor": "#2f9e44",
+ "backgroundColor": "#ffc9c9",
+ "width": 0,
+ "height": 40.40111211199792,
+ "seed": 1052632343,
+ "groupIds": [],
+ "frameId": null,
+ "roundness": {
+ "type": 2
+ },
+ "boundElements": [],
+ "updated": 1698927613071,
+ "link": null,
+ "locked": false,
+ "startBinding": null,
+ "endBinding": null,
+ "lastCommittedPoint": null,
+ "startArrowhead": null,
+ "endArrowhead": "arrow",
+ "points": [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ -40.40111211199792
+ ]
+ ]
+ },
+ {
+ "type": "arrow",
+ "version": 261,
+ "versionNonce": 693099385,
+ "isDeleted": false,
+ "id": "lwYvAs-7FTjcwxKjcx0KV",
+ "fillStyle": "solid",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 2,
+ "opacity": 100,
+ "angle": 0,
+ "x": 786.7392304423553,
+ "y": 649.6016935672433,
+ "strokeColor": "#2f9e44",
+ "backgroundColor": "#ffc9c9",
+ "width": 0,
+ "height": 40.40111211199792,
+ "seed": 1233043511,
+ "groupIds": [],
+ "frameId": null,
+ "roundness": {
+ "type": 2
+ },
+ "boundElements": [],
+ "updated": 1698927613071,
+ "link": null,
+ "locked": false,
+ "startBinding": {
+ "elementId": "s0PKxsWTJSDbQeEl_WI-C",
+ "focus": 0.016372633695398757,
+ "gap": 1
+ },
+ "endBinding": {
+ "elementId": "9ia1Uwc5X0fRw5iaahmcT",
+ "focus": 0.025318405829282714,
+ "gap": 14.862364635333904
+ },
+ "lastCommittedPoint": null,
+ "startArrowhead": null,
+ "endArrowhead": "arrow",
+ "points": [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ -40.40111211199792
+ ]
+ ]
+ },
+ {
+ "type": "text",
+ "version": 121,
+ "versionNonce": 952661143,
+ "isDeleted": false,
+ "id": "qWW8uxDIcV3Bkj28uvRLr",
+ "fillStyle": "solid",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 2,
+ "opacity": 100,
+ "angle": 0,
+ "x": 454.32425448306674,
+ "y": 537.8854189061962,
+ "strokeColor": "#1e1e1e",
+ "backgroundColor": "#ffc9c9",
+ "width": 93.75,
+ "height": 57.599999999999994,
+ "seed": 809847769,
+ "groupIds": [],
+ "frameId": null,
+ "roundness": null,
+ "boundElements": [],
+ "updated": 1698927613071,
+ "link": null,
+ "locked": false,
+ "fontSize": 16,
+ "fontFamily": 3,
+ "text": "patch\nrelease\ne.g. 3.3.8",
+ "textAlign": "center",
+ "verticalAlign": "top",
+ "containerId": null,
+ "originalText": "patch\nrelease\ne.g. 3.3.8",
+ "lineHeight": 1.2,
+ "baseline": 53
+ },
+ {
+ "type": "text",
+ "version": 257,
+ "versionNonce": 1838679129,
+ "isDeleted": false,
+ "id": "9ia1Uwc5X0fRw5iaahmcT",
+ "fillStyle": "solid",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 2,
+ "opacity": 100,
+ "angle": 0,
+ "x": 741.0510307156029,
+ "y": 536.7382168199114,
+ "strokeColor": "#1e1e1e",
+ "backgroundColor": "#ffc9c9",
+ "width": 93.75,
+ "height": 57.599999999999994,
+ "seed": 213765431,
+ "groupIds": [],
+ "frameId": null,
+ "roundness": null,
+ "boundElements": [
+ {
+ "id": "lwYvAs-7FTjcwxKjcx0KV",
+ "type": "arrow"
+ }
+ ],
+ "updated": 1698927613071,
+ "link": null,
+ "locked": false,
+ "fontSize": 16,
+ "fontFamily": 3,
+ "text": "patch\nrelease\ne.g. 3.3.9",
+ "textAlign": "center",
+ "verticalAlign": "top",
+ "containerId": null,
+ "originalText": "patch\nrelease\ne.g. 3.3.9",
+ "lineHeight": 1.2,
+ "baseline": 53
+ },
+ {
+ "type": "text",
+ "version": 222,
+ "versionNonce": 1528547767,
+ "isDeleted": false,
+ "id": "JALHBtowuh3_a86loej2x",
+ "fillStyle": "solid",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 2,
+ "opacity": 100,
+ "angle": 0,
+ "x": 350.7264132088442,
+ "y": 879.2675096875524,
+ "strokeColor": "#1e1e1e",
+ "backgroundColor": "#ffc9c9",
+ "width": 168.75,
+ "height": 57.599999999999994,
+ "seed": 41180921,
+ "groupIds": [],
+ "frameId": null,
+ "roundness": null,
+ "boundElements": [
+ {
+ "id": "mE8Mu0qKfFaWPCC5vmF_f",
+ "type": "arrow"
+ }
+ ],
+ "updated": 1698927613071,
+ "link": null,
+ "locked": false,
+ "fontSize": 16,
+ "fontFamily": 3,
+ "text": "pre minor\nrelease\ne.g. 3.4.0-alpha.1",
+ "textAlign": "center",
+ "verticalAlign": "top",
+ "containerId": null,
+ "originalText": "pre minor\nrelease\ne.g. 3.4.0-alpha.1",
+ "lineHeight": 1.2,
+ "baseline": 53
+ },
+ {
+ "type": "arrow",
+ "version": 345,
+ "versionNonce": 1286082873,
+ "isDeleted": false,
+ "id": "3heKY3vfe3-6ni4dX7Uqo",
+ "fillStyle": "solid",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 2,
+ "opacity": 100,
+ "angle": 0,
+ "x": 699.5281288163526,
+ "y": 831.0290882554708,
+ "strokeColor": "#f08c00",
+ "backgroundColor": "#ffc9c9",
+ "width": 0.5502191262773977,
+ "height": 33.25154356841597,
+ "seed": 627698359,
+ "groupIds": [],
+ "frameId": null,
+ "roundness": {
+ "type": 2
+ },
+ "boundElements": [],
+ "updated": 1698927613071,
+ "link": null,
+ "locked": false,
+ "startBinding": {
+ "elementId": "AtEf7o4WZQn4Zxq8EN5fH",
+ "focus": -0.05612657009295625,
+ "gap": 1.1451322685712295
+ },
+ "endBinding": {
+ "elementId": "9t6qH-tAxVUexkHHi2pd2",
+ "focus": 0.015156451076917755,
+ "gap": 15.586906139714358
+ },
+ "lastCommittedPoint": null,
+ "startArrowhead": null,
+ "endArrowhead": "arrow",
+ "points": [
+ [
+ 0,
+ 0
+ ],
+ [
+ -0.5502191262773977,
+ 33.25154356841597
+ ]
+ ]
+ },
+ {
+ "type": "text",
+ "version": 365,
+ "versionNonce": 1049066199,
+ "isDeleted": false,
+ "id": "9t6qH-tAxVUexkHHi2pd2",
+ "fillStyle": "solid",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 2,
+ "opacity": 100,
+ "angle": 0,
+ "x": 617.3409291322284,
+ "y": 879.8675379636011,
+ "strokeColor": "#1e1e1e",
+ "backgroundColor": "#ffc9c9",
+ "width": 159.375,
+ "height": 57.599999999999994,
+ "seed": 1013545943,
+ "groupIds": [],
+ "frameId": null,
+ "roundness": null,
+ "boundElements": [
+ {
+ "id": "3heKY3vfe3-6ni4dX7Uqo",
+ "type": "arrow"
+ }
+ ],
+ "updated": 1698927613071,
+ "link": null,
+ "locked": false,
+ "fontSize": 16,
+ "fontFamily": 3,
+ "text": "pre minor\nrelease\ne.g. 3.4.0-beta.1",
+ "textAlign": "center",
+ "verticalAlign": "top",
+ "containerId": null,
+ "originalText": "pre minor\nrelease\ne.g. 3.4.0-beta.1",
+ "lineHeight": 1.2,
+ "baseline": 53
+ },
+ {
+ "type": "arrow",
+ "version": 788,
+ "versionNonce": 1810072089,
+ "isDeleted": false,
+ "id": "vEF1cIIYYWKm84KLKqEz3",
+ "fillStyle": "solid",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 630.3597332113623,
+ "y": 667.2735668205443,
+ "strokeColor": "#f08c00",
+ "backgroundColor": "#ffc9c9",
+ "width": 2.258228100583324,
+ "height": 140.75112333166828,
+ "seed": 2091697367,
+ "groupIds": [],
+ "frameId": null,
+ "roundness": {
+ "type": 2
+ },
+ "boundElements": [],
+ "updated": 1698927613072,
+ "link": null,
+ "locked": false,
+ "startBinding": null,
+ "endBinding": {
+ "elementId": "4v23gkfhy-hzk18YdkfLz",
+ "focus": 0.13930391883256707,
+ "gap": 1.8256906627890626
+ },
+ "lastCommittedPoint": null,
+ "startArrowhead": null,
+ "endArrowhead": "arrow",
+ "points": [
+ [
+ 0,
+ 0
+ ],
+ [
+ 1.8426514015177418,
+ 69.09942755691065
+ ],
+ [
+ 2.258228100583324,
+ 140.75112333166828
+ ]
+ ]
+ },
+ {
+ "type": "arrow",
+ "version": 687,
+ "versionNonce": 2017318649,
+ "isDeleted": false,
+ "id": "M14Q0Uo1DBy2Ss2SOFSgW",
+ "fillStyle": "solid",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 370.5976915356099,
+ "y": 667.5155013947814,
+ "strokeColor": "#f08c00",
+ "backgroundColor": "#ffc9c9",
+ "width": 1.5329291446666957,
+ "height": 145.39303664953377,
+ "seed": 361678233,
+ "groupIds": [],
+ "frameId": null,
+ "roundness": {
+ "type": 2
+ },
+ "boundElements": [],
+ "updated": 1698927613072,
+ "link": null,
+ "locked": false,
+ "startBinding": null,
+ "endBinding": null,
+ "lastCommittedPoint": null,
+ "startArrowhead": null,
+ "endArrowhead": "arrow",
+ "points": [
+ [
+ 0,
+ 0
+ ],
+ [
+ -0.34892760581925586,
+ 83.56228079137543
+ ],
+ [
+ 1.1840015388474399,
+ 145.39303664953377
+ ]
+ ]
+ },
+ {
+ "type": "text",
+ "version": 537,
+ "versionNonce": 342487319,
+ "isDeleted": false,
+ "id": "CHAOOJMz7tNaG1VsG_uzT",
+ "fillStyle": "solid",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 2,
+ "opacity": 100,
+ "angle": 0,
+ "x": 384.81046417498214,
+ "y": 725.4677076298137,
+ "strokeColor": "#1e1e1e",
+ "backgroundColor": "#ffc9c9",
+ "width": 131.25,
+ "height": 57.599999999999994,
+ "seed": 1656007289,
+ "groupIds": [],
+ "frameId": null,
+ "roundness": null,
+ "boundElements": [],
+ "updated": 1698927613072,
+ "link": null,
+ "locked": false,
+ "fontSize": 16,
+ "fontFamily": 3,
+ "text": "merge main\ninto minor\nbefore release",
+ "textAlign": "left",
+ "verticalAlign": "top",
+ "containerId": null,
+ "originalText": "merge main\ninto minor\nbefore release",
+ "lineHeight": 1.2,
+ "baseline": 53
+ },
+ {
+ "type": "ellipse",
+ "version": 202,
+ "versionNonce": 876253145,
+ "isDeleted": false,
+ "id": "hDC6an14QljktaZCUhcPF",
+ "fillStyle": "solid",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 2,
+ "opacity": 100,
+ "angle": 0,
+ "x": 993.0386151813434,
+ "y": 810.335845473903,
+ "strokeColor": "#1e1e1e",
+ "backgroundColor": "#ffc9c9",
+ "width": 18.814646969963974,
+ "height": 18.814646969963974,
+ "seed": 1433430105,
+ "groupIds": [],
+ "frameId": null,
+ "roundness": {
+ "type": 2
+ },
+ "boundElements": [
+ {
+ "id": "Gao2krnDddLMCj468JSWD",
+ "type": "arrow"
+ }
+ ],
+ "updated": 1698927613072,
+ "link": null,
+ "locked": false
+ },
+ {
+ "type": "arrow",
+ "version": 1525,
+ "versionNonce": 777631287,
+ "isDeleted": false,
+ "id": "ces8IwHCpQlTnELpjFDIn",
+ "fillStyle": "solid",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 2,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1092.5386800881793,
+ "y": 827.5114796878765,
+ "strokeColor": "#f08c00",
+ "backgroundColor": "#ffc9c9",
+ "width": 0.3315362017829102,
+ "height": 49.45191086419197,
+ "seed": 225867737,
+ "groupIds": [],
+ "frameId": null,
+ "roundness": {
+ "type": 2
+ },
+ "boundElements": [],
+ "updated": 1698927613072,
+ "link": null,
+ "locked": false,
+ "startBinding": null,
+ "endBinding": {
+ "elementId": "8rWUxp-jRNGrGRmhHHfm4",
+ "focus": -0.2047594653982401,
+ "gap": 10.392197401393389
+ },
+ "lastCommittedPoint": null,
+ "startArrowhead": null,
+ "endArrowhead": "arrow",
+ "points": [
+ [
+ 0,
+ 0
+ ],
+ [
+ -0.3315362017829102,
+ 49.45191086419197
+ ]
+ ]
+ },
+ {
+ "type": "text",
+ "version": 894,
+ "versionNonce": 1173171385,
+ "isDeleted": false,
+ "id": "8rWUxp-jRNGrGRmhHHfm4",
+ "fillStyle": "solid",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 2,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1047.251646167428,
+ "y": 887.3555879534618,
+ "strokeColor": "#1e1e1e",
+ "backgroundColor": "#ffc9c9",
+ "width": 112.5,
+ "height": 57.599999999999994,
+ "seed": 1600918713,
+ "groupIds": [],
+ "frameId": null,
+ "roundness": null,
+ "boundElements": [
+ {
+ "id": "ces8IwHCpQlTnELpjFDIn",
+ "type": "arrow"
+ }
+ ],
+ "updated": 1698927613072,
+ "link": null,
+ "locked": false,
+ "fontSize": 16,
+ "fontFamily": 3,
+ "text": "stable minor\nrelease\ne.g. 3.4.0",
+ "textAlign": "center",
+ "verticalAlign": "top",
+ "containerId": null,
+ "originalText": "stable minor\nrelease\ne.g. 3.4.0",
+ "lineHeight": 1.2,
+ "baseline": 53
+ },
+ {
+ "type": "ellipse",
+ "version": 201,
+ "versionNonce": 78435447,
+ "isDeleted": false,
+ "id": "3RHuRn_evSK0YUe02B4MY",
+ "fillStyle": "solid",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 2,
+ "opacity": 100,
+ "angle": 0,
+ "x": 909.9742423218671,
+ "y": 810.4142561718397,
+ "strokeColor": "#2f9e44",
+ "backgroundColor": "#b2f2bb",
+ "width": 18.814646969963974,
+ "height": 18.814646969963974,
+ "seed": 1199705047,
+ "groupIds": [],
+ "frameId": null,
+ "roundness": {
+ "type": 2
+ },
+ "boundElements": [],
+ "updated": 1698927613072,
+ "link": null,
+ "locked": false
+ },
+ {
+ "type": "ellipse",
+ "version": 371,
+ "versionNonce": 2093872087,
+ "isDeleted": false,
+ "id": "9h2Cu__8owLUgUGjGcWDe",
+ "fillStyle": "solid",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 2,
+ "opacity": 100,
+ "angle": 0,
+ "x": 848.4414471158692,
+ "y": 650.826922928275,
+ "strokeColor": "#1e1e1e",
+ "backgroundColor": "#b2f2bb",
+ "width": 18.814646969963974,
+ "height": 18.814646969963974,
+ "seed": 603147257,
+ "groupIds": [],
+ "frameId": null,
+ "roundness": {
+ "type": 2
+ },
+ "boundElements": [],
+ "updated": 1698927613072,
+ "link": null,
+ "locked": false
+ },
+ {
+ "type": "ellipse",
+ "version": 361,
+ "versionNonce": 1981618457,
+ "isDeleted": false,
+ "id": "s0PKxsWTJSDbQeEl_WI-C",
+ "fillStyle": "solid",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 2,
+ "opacity": 100,
+ "angle": 0,
+ "x": 777.1778842958995,
+ "y": 650.2466837635417,
+ "strokeColor": "#2f9e44",
+ "backgroundColor": "#b2f2bb",
+ "width": 18.814646969963974,
+ "height": 18.814646969963974,
+ "seed": 326722777,
+ "groupIds": [],
+ "frameId": null,
+ "roundness": {
+ "type": 2
+ },
+ "boundElements": [
+ {
+ "id": "lwYvAs-7FTjcwxKjcx0KV",
+ "type": "arrow"
+ }
+ ],
+ "updated": 1698927613072,
+ "link": null,
+ "locked": false
+ },
+ {
+ "type": "text",
+ "version": 871,
+ "versionNonce": 1528156247,
+ "isDeleted": false,
+ "id": "3JAdSa7kqqSDSom5ZFDoE",
+ "fillStyle": "solid",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 2,
+ "opacity": 100,
+ "angle": 0,
+ "x": 904.3603861670398,
+ "y": 707.2413714353705,
+ "strokeColor": "#1e1e1e",
+ "backgroundColor": "#ffc9c9",
+ "width": 140.625,
+ "height": 57.599999999999994,
+ "seed": 1011049431,
+ "groupIds": [],
+ "frameId": null,
+ "roundness": null,
+ "boundElements": [],
+ "updated": 1698927613072,
+ "link": null,
+ "locked": false,
+ "fontSize": 16,
+ "fontFamily": 3,
+ "text": "final merge\nmain into minor\nbefore release",
+ "textAlign": "center",
+ "verticalAlign": "top",
+ "containerId": null,
+ "originalText": "final merge\nmain into minor\nbefore release",
+ "lineHeight": 1.2,
+ "baseline": 53
+ },
+ {
+ "type": "arrow",
+ "version": 591,
+ "versionNonce": 1714373785,
+ "isDeleted": false,
+ "id": "7kFBLq2Iczmj0lVnVk8Ad",
+ "fillStyle": "solid",
+ "strokeWidth": 2,
+ "strokeStyle": "dotted",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1100.7141458557703,
+ "y": 814.2034531496416,
+ "strokeColor": "#2f9e44",
+ "backgroundColor": "#ffffff",
+ "width": 127.38209933342364,
+ "height": 144.5383600420214,
+ "seed": 25829591,
+ "groupIds": [],
+ "frameId": null,
+ "roundness": {
+ "type": 2
+ },
+ "boundElements": [],
+ "updated": 1698927613072,
+ "link": null,
+ "locked": false,
+ "startBinding": null,
+ "endBinding": {
+ "elementId": "Y7VXnuc9QEz2N2l9i0xrc",
+ "focus": 0.3932764551319699,
+ "gap": 5.928572790502042
+ },
+ "lastCommittedPoint": null,
+ "startArrowhead": null,
+ "endArrowhead": "arrow",
+ "points": [
+ [
+ 0,
+ 0
+ ],
+ [
+ 88.94909573964219,
+ -43.721805169626464
+ ],
+ [
+ 127.38209933342364,
+ -144.5383600420214
+ ]
+ ]
+ },
+ {
+ "type": "text",
+ "version": 1208,
+ "versionNonce": 1254600055,
+ "isDeleted": false,
+ "id": "gwFWlPLabuYhxCOweJjWz",
+ "fillStyle": "solid",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 2,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1223.0464288187204,
+ "y": 725.1565933898091,
+ "strokeColor": "#1e1e1e",
+ "backgroundColor": "#ffc9c9",
+ "width": 150,
+ "height": 38.4,
+ "seed": 51102743,
+ "groupIds": [],
+ "frameId": null,
+ "roundness": null,
+ "boundElements": [],
+ "updated": 1698927613072,
+ "link": null,
+ "locked": false,
+ "fontSize": 16,
+ "fontFamily": 3,
+ "text": "main merge minor\n(fast forward)",
+ "textAlign": "center",
+ "verticalAlign": "top",
+ "containerId": null,
+ "originalText": "main merge minor\n(fast forward)",
+ "lineHeight": 1.2,
+ "baseline": 34
+ },
+ {
+ "type": "ellipse",
+ "version": 597,
+ "versionNonce": 1760381305,
+ "isDeleted": false,
+ "id": "Y7VXnuc9QEz2N2l9i0xrc",
+ "fillStyle": "solid",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 2,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1227.4473966637659,
+ "y": 647.6689320688656,
+ "strokeColor": "#1e1e1e",
+ "backgroundColor": "#a5d8ff",
+ "width": 18.814646969963974,
+ "height": 18.814646969963974,
+ "seed": 412038615,
+ "groupIds": [],
+ "frameId": null,
+ "roundness": {
+ "type": 2
+ },
+ "boundElements": [
+ {
+ "id": "7kFBLq2Iczmj0lVnVk8Ad",
+ "type": "arrow"
+ }
+ ],
+ "updated": 1698927613072,
+ "link": null,
+ "locked": false
+ },
+ {
+ "type": "ellipse",
+ "version": 547,
+ "versionNonce": 1585505943,
+ "isDeleted": false,
+ "id": "ggogfJT7E_bbfEog7Hjnp",
+ "fillStyle": "solid",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 2,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1083.7911569735343,
+ "y": 809.5203742153592,
+ "strokeColor": "#1e1e1e",
+ "backgroundColor": "#a5d8ff",
+ "width": 18.814646969963974,
+ "height": 18.814646969963974,
+ "seed": 741463161,
+ "groupIds": [],
+ "frameId": null,
+ "roundness": {
+ "type": 2
+ },
+ "boundElements": [
+ {
+ "id": "nRDWQs5nQa37yzCWTBiXC",
+ "type": "arrow"
+ }
+ ],
+ "updated": 1698927613072,
+ "link": null,
+ "locked": false
+ },
+ {
+ "type": "text",
+ "version": 229,
+ "versionNonce": 1935127129,
+ "isDeleted": false,
+ "id": "eU-EgpwDD42CLYUEIDLaD",
+ "fillStyle": "solid",
+ "strokeWidth": 2,
+ "strokeStyle": "dotted",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 305.8405004265049,
+ "y": 389.31989430571576,
+ "strokeColor": "#1e1e1e",
+ "backgroundColor": "#a5d8ff",
+ "width": 581.25,
+ "height": 19.2,
+ "seed": 1086231577,
+ "groupIds": [],
+ "frameId": null,
+ "roundness": null,
+ "boundElements": [],
+ "updated": 1698927613072,
+ "link": null,
+ "locked": false,
+ "fontSize": 16,
+ "fontFamily": 3,
+ "text": "- merge feature PRs into, and release minors from minor branch",
+ "textAlign": "left",
+ "verticalAlign": "top",
+ "containerId": null,
+ "originalText": "- merge feature PRs into, and release minors from minor branch",
+ "lineHeight": 1.2,
+ "baseline": 15
+ },
+ {
+ "type": "text",
+ "version": 397,
+ "versionNonce": 116088535,
+ "isDeleted": false,
+ "id": "Kt6VBAVD4sLM4IexsRGoX",
+ "fillStyle": "solid",
+ "strokeWidth": 2,
+ "strokeStyle": "dotted",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 305.4136207977353,
+ "y": 358.61173442109686,
+ "strokeColor": "#1e1e1e",
+ "backgroundColor": "#a5d8ff",
+ "width": 618.75,
+ "height": 19.2,
+ "seed": 273353945,
+ "groupIds": [],
+ "frameId": null,
+ "roundness": null,
+ "boundElements": [],
+ "updated": 1698927617946,
+ "link": null,
+ "locked": false,
+ "fontSize": 16,
+ "fontFamily": 3,
+ "text": "- merge fix / chore PRs into, and release patches from main branch",
+ "textAlign": "left",
+ "verticalAlign": "top",
+ "containerId": null,
+ "originalText": "- merge fix / chore PRs into, and release patches from main branch",
+ "lineHeight": 1.2,
+ "baseline": 15
+ },
+ {
+ "type": "text",
+ "version": 459,
+ "versionNonce": 440532793,
+ "isDeleted": false,
+ "id": "JwKEdnU6H_Nu74WbEAX5M",
+ "fillStyle": "solid",
+ "strokeWidth": 2,
+ "strokeStyle": "dotted",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 305.6723761009271,
+ "y": 418.3724478537203,
+ "strokeColor": "#1e1e1e",
+ "backgroundColor": "#a5d8ff",
+ "width": 459.375,
+ "height": 19.2,
+ "seed": 1001222329,
+ "groupIds": [],
+ "frameId": null,
+ "roundness": null,
+ "boundElements": [],
+ "updated": 1698927613072,
+ "link": null,
+ "locked": false,
+ "fontSize": 16,
+ "fontFamily": 3,
+ "text": "- merge main into minor before each minor release",
+ "textAlign": "left",
+ "verticalAlign": "top",
+ "containerId": null,
+ "originalText": "- merge main into minor before each minor release",
+ "lineHeight": 1.2,
+ "baseline": 15
+ },
+ {
+ "type": "text",
+ "version": 602,
+ "versionNonce": 1108720119,
+ "isDeleted": false,
+ "id": "mb9ZoP803MiH7MTO8wH-2",
+ "fillStyle": "solid",
+ "strokeWidth": 2,
+ "strokeStyle": "dotted",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 305.0895924262568,
+ "y": 447.44321411383333,
+ "strokeColor": "#1e1e1e",
+ "backgroundColor": "#a5d8ff",
+ "width": 534.375,
+ "height": 19.2,
+ "seed": 264651479,
+ "groupIds": [],
+ "frameId": null,
+ "roundness": null,
+ "boundElements": [],
+ "updated": 1698927613072,
+ "link": null,
+ "locked": false,
+ "fontSize": 16,
+ "fontFamily": 3,
+ "text": "- fast forward main to minor after a stable minor release",
+ "textAlign": "left",
+ "verticalAlign": "top",
+ "containerId": null,
+ "originalText": "- fast forward main to minor after a stable minor release",
+ "lineHeight": 1.2,
+ "baseline": 15
+ },
+ {
+ "type": "text",
+ "version": 612,
+ "versionNonce": 1588872441,
+ "isDeleted": false,
+ "id": "IfJPOFiwrCibpaBQqc5g-",
+ "fillStyle": "solid",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 2,
+ "opacity": 100,
+ "angle": 0,
+ "x": 646.7131179044119,
+ "y": 724.4984335940012,
+ "strokeColor": "#1e1e1e",
+ "backgroundColor": "#ffc9c9",
+ "width": 131.25,
+ "height": 57.599999999999994,
+ "seed": 1301100087,
+ "groupIds": [],
+ "frameId": null,
+ "roundness": null,
+ "boundElements": [],
+ "updated": 1698927613072,
+ "link": null,
+ "locked": false,
+ "fontSize": 16,
+ "fontFamily": 3,
+ "text": "merge main\ninto minor\nbefore release",
+ "textAlign": "left",
+ "verticalAlign": "top",
+ "containerId": null,
+ "originalText": "merge main\ninto minor\nbefore release",
+ "lineHeight": 1.2,
+ "baseline": 53
+ }
+ ],
+ "appState": {
+ "gridSize": null,
+ "viewBackgroundColor": "#ffffff"
+ },
+ "files": {}
+}
\ No newline at end of file
diff --git a/.github/git-branch-workflow.png b/.github/git-branch-workflow.png
new file mode 100644
index 000000000..6c8ee07d4
Binary files /dev/null and b/.github/git-branch-workflow.png differ
diff --git a/.github/issue-workflow.png b/.github/issue-workflow.png
new file mode 100644
index 000000000..92b1de063
Binary files /dev/null and b/.github/issue-workflow.png differ
diff --git a/.github/maintenance.md b/.github/maintenance.md
new file mode 100644
index 000000000..b1fb550dd
--- /dev/null
+++ b/.github/maintenance.md
@@ -0,0 +1,123 @@
+# Vue Core Maintenance Handbook
+
+Unlike [contributing.md](./contributing.md), which targets external contributors, this document is mainly intended for team members responsible for maintaining the project. It provides guidelines on how to triage issues, review & merge PRs, and publish releases. However, it should also be valuable to external contributors even if you are not a maintainer, as it gives you a better idea of how the maintainers operate, and how you can better collaborate with them. And who knows - maybe one day you will join as a maintainer as well!
+
+- [Issue Triage Workflow](#issue-triage-workflow)
+- [Pull Request Review Guidelines](#pull-request-review-guidelines)
+ - [Reviewing a Fix](#reviewing-a-fix)
+ - [Reviewing a Refactor](#reviewing-a-refactor)
+ - [Reviewing a Feature](#reviewing-a-feature)
+ - [Common Considerations for All PRs](#common-considerations-for-all-prs)
+- [PR Merge Rules for Team Members](#pr-merge-rules-for-team-members)
+- [Git Branch and Release Workflow](#git-branch-and-release-workflow)
+
+## Issue Triage Workflow
+
+
+
+## Pull Request Review Guidelines
+
+The first step of reviewing a PR is to identify its purpose. We can usually put a PR in one of these categories:
+
+- **Fix**: fixes some wrong behavior. Usually associated with an issue that has a reproduction of the behavior being fixed.
+- **Refactor**: improves performance or code quality, but does not affect behavior.
+- **Feature**: implements something that increases the public API surface.
+
+Depending on the type of the PR, different considerations need to be taken into account.
+
+### Reviewing a Fix
+
+- Is the PR fixing a well defined issue / bug report?
+ - If not, ask to clarify context / provide reproduction or failing test case
+- In most cases, a fix PR should include a test case that fails without the fix.
+- Is it the right fix?
+ - If not, guide user to rework the PR.
+ - If the needed change is small and obvious, can directly push to the PR or add inline suggestions to reduce the back-and-forth.
+- Is the cost justified?
+ - Sometimes the fix for a rare edge case might be introducing disproportionately large overhead (perf or code size). We should try our best to reduce the overhead to make the fix a reasonable tradeoff.
+- If the reviewer is not sure about a fix, try to leave a comment explaining the concerns / reservations so the contributor at least gets some feedback.
+
+#### Verifying a Fix
+
+- **Always locally verify that the fix indeed fixes the original behavior, either through a reproduction or a failing test case.**
+- We will run [ecosystem-ci](https://github.com/vuejs/ecosystem-ci) before every release, but if you are concerned about the potential impact of a change, it never hurts to manually run ecosystem-ci by leaving a `/ecosystem-ci run` comment (only works for team members).
+- Take extra caution with snapshot tests! The CI can be "passing" even if the code generated in the snapshot contains bugs. It's best to always accompany a snapshot test with extra `expect(code).toMatch(...)` assertions.
+
+### Reviewing a Refactor
+
+- Performance: if a refactor PR claims to improve performance, there should be benchmarks showcasing said performance unless the improvement is self-explanatory.
+
+- Code quality / stylistic PRs: we should be conservative on merging this type PRs because (1) they can be subjective in many cases, and (2) they often come with large git diffs, causing merge conflicts with other pending PRs, and leading to unwanted noise when tracing changes through git history. Use your best judgement on this type of PRs on whether they are worth it.
+
+ - For PRs in this category that are approved, do not merge immediately. Group them before releasing a new minor, after all feature-oriented PRs are merged.
+
+### Reviewing a Feature
+
+- Feature PRs should always have clear context and explanation on why the feature should be added, ideally in the form of an RFC. If the PR doesn't explain what real-world problem it is solving, ask the contributor to clarify.
+
+- Decide if the feature should require an RFC process. The line isn't always clear, but a rough criteria is whether it is augmenting an existing API vs. adding a new API. Some examples:
+
+ - Adding a new built-in component or directive is "significant" and definitely requires an RFC.
+ - Template syntax additions like adding a new `v-on` modifier or a new `v-bind` syntax sugar are "substantial". It would be nice to have an RFC for it, but a detailed explanation on the use case and reasoning behind the design directly in the PR itself can be acceptable.
+ - Small, low-impact additions like exposing a new utility type or adding a new app config option can be self-explanatory, but should still provide enough context in the PR.
+
+- Always ask if the use case can be solved with existing APIs. Vue already has a pretty large API surface, so we want to make sure every new addition either solves something that wasn't possible before, or significantly improves the DX of a common task.
+
+### Common Considerations for All PRs
+
+- Scope: a PR should only contain changes directly related to the problem being addressed. It should not contain unnecessary code style changes.
+
+- Implementation: code style should be consistent with the rest of the codebase, follow common best practices. Prefer code that is boring but easy to understand over "clever" code.
+
+- Size: bundle size matters. We have a GitHub action that compares the size change for every PR. We should always aim to realize the desired changes with the smallest amount of code size increase.
+
+ - Sometimes we need to compare the size increase vs. perceived benefits to decide whether a change is justifiable. Also take extra care to make sure added code can be tree-shaken if not needed.
+
+ - Make sure to put dev-only code in `__DEV__` branches so they are tree-shakable.
+
+ - Runtime code is more sensitive to size increase than compiler code.
+
+ - Make sure it doesn't accidentally cause dev-only or compiler-only code branches to be included in the runtime build. Notable case is that some functions in @vue/shared are compiler-only and should not be used in runtime code, e.g. `isHTMLTag` and `isSVGTag`.
+
+- Performance
+
+ - Be careful about code changes in "hot paths", in particular the Virtual DOM renderer (`runtime-core/src/renderer.ts`) and component instantiation code.
+
+- Potential Breakage
+ - avoiding runtime behavior breakage is the highest priority
+ - if not sure, use `ecosystem-ci` to verify!
+ - some fix inevitably cause behavior change, these must be discussed case-by-case
+ - type level breakage (e.g upgrading TS) is possible between minors
+
+## PR Merge Rules for Team Members
+
+Given that the PR meets the review requirements:
+
+- Chore / dependencies bumps: can merge directly.
+- Fixes / refactors: can merge with two or more approvals from team members.
+ - If you believe a PR looks good but you are not 100% confident to merge, label with "ready for merge" and Evan will provide a final review before merging.
+- Features: if approved by two or more team members, label with "ready to merge". Evan will review periodically, or they can be raised and discussed at team meetings.
+
+## Git Branch and Release Workflow
+
+We use two primary work branches: `main` and `minor`.
+
+- The `main` branch is for stable releases. Changes that are bug fixes or refactors that do not affect the public API surface should land in this branch. We periodically release patch releases from the `main` branch.
+
+- The `minor` branch is the WIP branch for the next minor release. Changes that are new features or those that affect public API behavior should land in this branch. We will periodically release pre-releases (alpha / beta) for the next minor from this branch.
+
+Before each release, we merge latest `main` into `minor` so it would include the latest bug fixes.
+
+When the minor is ready, we do a final merge of `main` into `minor`, and then release a stable minor from this branch (e.g. `3.4.0`). After that, the `main` branch is fast-forwarded to the release commit, so the two branches are synced at each stable minor release.
+
+
+
+### Reasoning Behind the Workflow
+
+The reason behind this workflow is to allow merging and releasing of fixes and features in parallel. In the past, we used a linear trunk-based development model. While the linear model results in a clean git history, the downside is that we need to be careful about when to merge patches vs. features.
+
+Vue typically groups a number of features with the same scope in a minor release. We don't want to release a minor just because we happened to merge a feature PR along with a bunch of small bug fixes. So we usually "wait" until we feel we are ready to start working on a minor release before merging feature PRs.
+
+But in reality, there are always bugs to fix and patch release to work on - this caused the intervals between minors to drag on longer than we had hoped, and many feature PRs were left waiting for a long period of time.
+
+This is why we decided to separate bug fixes and feature PRs into separate branches. With this two-branch model, we are able to merge and release both types of changes in parallel.
diff --git a/.github/renovate.json5 b/.github/renovate.json5
new file mode 100644
index 000000000..8808b599e
--- /dev/null
+++ b/.github/renovate.json5
@@ -0,0 +1,66 @@
+{
+ $schema: 'https://docs.renovatebot.com/renovate-schema.json',
+ extends: ['config:base', 'schedule:weekly', 'group:allNonMajor'],
+ labels: ['dependencies'],
+ ignorePaths: ['**/__tests__/**'],
+ rangeStrategy: 'bump',
+ packageRules: [
+ {
+ depTypeList: ['peerDependencies'],
+ enabled: false,
+ },
+ {
+ groupName: 'test',
+ matchPackageNames: ['vitest', 'jsdom', 'puppeteer'],
+ matchPackagePrefixes: ['@vitest'],
+ },
+ {
+ groupName: 'playground',
+ matchFileNames: [
+ 'packages-private/sfc-playground/package.json',
+ 'packages-private/template-explorer/package.json',
+ ],
+ },
+ {
+ groupName: 'compiler',
+ matchPackageNames: ['magic-string'],
+ matchPackagePrefixes: ['@babel', 'postcss'],
+ },
+ {
+ groupName: 'build',
+ matchPackageNames: ['vite', '@swc/core'],
+ matchPackagePrefixes: ['rollup', 'esbuild', '@rollup', '@vitejs'],
+ },
+ {
+ groupName: 'lint',
+ matchPackageNames: ['simple-git-hooks', 'lint-staged'],
+ matchPackagePrefixes: ['typescript-eslint', 'eslint', 'prettier'],
+ },
+ ],
+ ignoreDeps: [
+ 'vue',
+
+ // manually bumping
+ 'node',
+ 'typescript',
+
+ // ESM only
+ 'estree-walker',
+
+ // pinned
+ // https://github.com/vuejs/core/issues/10300#issuecomment-1940855364
+ 'lru-cache',
+
+ // pinned
+ // https://github.com/vuejs/core/commit/a012e39b373f1b6918e5c89856e8f902e1bfa14d
+ '@rollup/plugin-replace',
+
+ // pinned
+ // only used in example for e2e tests
+ 'marked',
+
+ // pinned, 5.0+ has exports issues
+ // https://github.com/vuejs/core/issues/11603
+ 'entities',
+ ],
+}
diff --git a/.github/workflows/autofix.yml b/.github/workflows/autofix.yml
new file mode 100644
index 000000000..d5c31beff
--- /dev/null
+++ b/.github/workflows/autofix.yml
@@ -0,0 +1,34 @@
+name: autofix.ci
+
+on:
+ pull_request:
+permissions:
+ contents: read
+
+jobs:
+ autofix:
+ runs-on: ubuntu-latest
+ env:
+ PUPPETEER_SKIP_DOWNLOAD: 'true'
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Install pnpm
+ uses: pnpm/action-setup@v4.0.0
+
+ - name: Install Node.js
+ uses: actions/setup-node@v4
+ with:
+ node-version-file: '.node-version'
+ registry-url: 'https://registry.npmjs.org'
+ cache: 'pnpm'
+
+ - run: pnpm install
+
+ - name: Run eslint
+ run: pnpm run lint --fix
+
+ - name: Run prettier
+ run: pnpm run format
+
+ - uses: autofix-ci/action@ff86a557419858bb967097bfc916833f5647fa8c
diff --git a/.github/workflows/canary-minor.yml b/.github/workflows/canary-minor.yml
new file mode 100644
index 000000000..b5d75b9ce
--- /dev/null
+++ b/.github/workflows/canary-minor.yml
@@ -0,0 +1,33 @@
+name: canary minor release
+on:
+ # Runs every Monday at 1 AM UTC (9:00 AM in Singapore)
+ schedule:
+ - cron: 0 1 * * MON
+ workflow_dispatch:
+
+jobs:
+ canary:
+ # prevents this action from running on forks
+ if: github.repository == 'vuejs/core'
+ runs-on: ubuntu-latest
+ environment: Release
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ ref: minor
+
+ - name: Install pnpm
+ uses: pnpm/action-setup@v4.0.0
+
+ - name: Install Node.js
+ uses: actions/setup-node@v4
+ with:
+ node-version-file: '.node-version'
+ registry-url: 'https://registry.npmjs.org'
+ cache: 'pnpm'
+
+ - run: pnpm install
+
+ - run: pnpm release --canary --publish --tag minor
+ env:
+ NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
diff --git a/.github/workflows/canary.yml b/.github/workflows/canary.yml
new file mode 100644
index 000000000..bb622725a
--- /dev/null
+++ b/.github/workflows/canary.yml
@@ -0,0 +1,31 @@
+name: canary release
+on:
+ # Runs every Monday at 1 AM UTC (9:00 AM in Singapore)
+ schedule:
+ - cron: 0 1 * * MON
+ workflow_dispatch:
+
+jobs:
+ canary:
+ # prevents this action from running on forks
+ if: github.repository == 'vuejs/core'
+ runs-on: ubuntu-latest
+ environment: Release
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Install pnpm
+ uses: pnpm/action-setup@v4.0.0
+
+ - name: Install Node.js
+ uses: actions/setup-node@v4
+ with:
+ node-version-file: '.node-version'
+ registry-url: 'https://registry.npmjs.org'
+ cache: 'pnpm'
+
+ - run: pnpm install
+
+ - run: pnpm release --canary --publish
+ env:
+ NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 085c8dbbd..c8c217f62 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -3,76 +3,40 @@ on:
push:
branches:
- '**'
+ tags:
+ - '!**'
pull_request:
branches:
- - master
+ - main
+ - minor
+
jobs:
test:
+ if: ${{ ! startsWith(github.event.head_commit.message, 'release:') && (github.event_name == 'push' || github.event.pull_request.head.repo.full_name != github.repository) }}
+ uses: ./.github/workflows/test.yml
+
+ continuous-release:
+ if: github.repository == 'vuejs/core'
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v2
+ - name: Checkout
+ uses: actions/checkout@v4
- name: Install pnpm
- uses: pnpm/action-setup@v2.0.1
- with:
- version: 6.15.1
+ uses: pnpm/action-setup@v4
- - name: Set node version to 16
- uses: actions/setup-node@v2
+ - name: Install Node.js
+ uses: actions/setup-node@v4
with:
- node-version: 16
+ node-version-file: '.node-version'
+ registry-url: 'https://registry.npmjs.org'
cache: 'pnpm'
- - run: pnpm install
+ - name: Install deps
+ run: pnpm install
- - name: Run unit tests
- run: pnpm run test
+ - name: Build
+ run: pnpm build --withTypes
- test-dts:
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v2
-
- - name: Install pnpm
- uses: pnpm/action-setup@v2.0.1
- with:
- version: 6.15.1
-
- - name: Set node version to 16
- uses: actions/setup-node@v2
- with:
- node-version: 16
- cache: 'pnpm'
-
- - run: pnpm install
-
- - name: Run type declaration tests
- run: pnpm run test-dts
-
- size:
- runs-on: ubuntu-latest
- env:
- CI_JOB_NUMBER: 1
- steps:
- - uses: actions/checkout@v2
-
- - name: Install pnpm
- uses: pnpm/action-setup@v2.0.1
- with:
- version: 6.15.1
-
- - name: Set node version to 16
- uses: actions/setup-node@v2
- with:
- node-version: 16
- cache: 'pnpm'
-
- - run: pnpm install
- - run: pnpm run size
-
- # - name: Check build size
- # uses: posva/size-check-action@v1.1.2
- # with:
- # github_token: ${{ secrets.GITHUB_TOKEN }}
- # build_script: size
- # files: packages/vue/dist/vue.global.prod.js packages/runtime-dom/dist/runtime-dom.global.prod.js packages/size-check/dist/index.js
+ - name: Release
+ run: pnpx pkg-pr-new publish --compact --pnpm './packages/*'
diff --git a/.github/workflows/close-cant-reproduce-issues.yml b/.github/workflows/close-cant-reproduce-issues.yml
new file mode 100644
index 000000000..8fb48f842
--- /dev/null
+++ b/.github/workflows/close-cant-reproduce-issues.yml
@@ -0,0 +1,21 @@
+name: Auto close issues with "can't reproduce" label
+
+on:
+ schedule:
+ - cron: '0 0 * * *'
+
+permissions:
+ issues: write
+
+jobs:
+ close-issues:
+ if: github.repository == 'vuejs/core'
+ runs-on: ubuntu-latest
+ steps:
+ - name: can't reproduce
+ uses: actions-cool/issues-helper@v3
+ with:
+ actions: 'close-issues'
+ token: ${{ secrets.GITHUB_TOKEN }}
+ labels: "can't reproduce"
+ inactive-day: 3
diff --git a/.github/workflows/ecosystem-ci-trigger.yml b/.github/workflows/ecosystem-ci-trigger.yml
new file mode 100644
index 000000000..b3e963ece
--- /dev/null
+++ b/.github/workflows/ecosystem-ci-trigger.yml
@@ -0,0 +1,90 @@
+name: ecosystem-ci trigger
+
+on:
+ issue_comment:
+ types: [created]
+
+jobs:
+ trigger:
+ runs-on: ubuntu-latest
+ if: github.repository == 'vuejs/core' && github.event.issue.pull_request && startsWith(github.event.comment.body, '/ecosystem-ci run')
+ steps:
+ - name: Check user permission
+ uses: actions/github-script@v7
+ with:
+ script: |
+ const user = context.payload.sender.login
+ console.log(`Validate user: ${user}`)
+
+ let isVuejsMember = false
+ try {
+ const { status } = await github.rest.orgs.checkMembershipForUser({
+ org: 'vuejs',
+ username: user
+ });
+
+ isVuejsMember = (status === 204)
+ } catch (e) {}
+
+ if (isVuejsMember) {
+ console.log('Allowed')
+ await github.rest.reactions.createForIssueComment({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ comment_id: context.payload.comment.id,
+ content: '+1',
+ })
+ } else {
+ console.log('Not allowed')
+ await github.rest.reactions.createForIssueComment({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ comment_id: context.payload.comment.id,
+ content: '-1',
+ })
+ throw new Error('not allowed')
+ }
+ - name: Get PR info
+ uses: actions/github-script@v7
+ id: get-pr-data
+ with:
+ script: |
+ console.log(`Get PR info: ${context.repo.owner}/${context.repo.repo}#${context.issue.number}`)
+ const { data: pr } = await github.rest.pulls.get({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ pull_number: context.issue.number
+ })
+ return {
+ num: context.issue.number,
+ branchName: pr.head.ref,
+ repo: pr.head.repo.full_name,
+ commit: pr.head.sha
+ }
+ - name: Trigger run
+ uses: actions/github-script@v7
+ id: trigger
+ env:
+ COMMENT: ${{ github.event.comment.body }}
+ with:
+ github-token: ${{ secrets.ECOSYSTEM_CI_ACCESS_TOKEN }}
+ result-encoding: string
+ script: |
+ const comment = process.env.COMMENT.trim()
+ const prData = ${{ steps.get-pr-data.outputs.result }}
+
+ const suite = comment.replace(/^\/ecosystem-ci run/, '').trim()
+
+ await github.rest.actions.createWorkflowDispatch({
+ owner: context.repo.owner,
+ repo: 'ecosystem-ci',
+ workflow_id: 'ecosystem-ci-from-pr.yml',
+ ref: 'main',
+ inputs: {
+ prNumber: '' + prData.num,
+ branchName: prData.branchName,
+ repo: prData.repo,
+ suite: suite === '' ? '-' : suite,
+ commit: prData.commit
+ }
+ })
diff --git a/.github/workflows/lock-closed-issues.yml b/.github/workflows/lock-closed-issues.yml
new file mode 100644
index 000000000..68a7d6c7a
--- /dev/null
+++ b/.github/workflows/lock-closed-issues.yml
@@ -0,0 +1,20 @@
+name: Lock Closed Issues
+
+on:
+ schedule:
+ - cron: '0 0 * * *'
+
+permissions:
+ issues: write
+
+jobs:
+ action:
+ if: github.repository == 'vuejs/core'
+ runs-on: ubuntu-latest
+ steps:
+ - uses: dessant/lock-threads@v5
+ with:
+ github-token: ${{ secrets.GITHUB_TOKEN }}
+ issue-inactive-days: '14'
+ issue-lock-reason: ''
+ process-only: 'issues'
diff --git a/.github/workflows/release-tag.yml b/.github/workflows/release-tag.yml
deleted file mode 100644
index 8781b44c8..000000000
--- a/.github/workflows/release-tag.yml
+++ /dev/null
@@ -1,23 +0,0 @@
-on:
- push:
- tags:
- - 'v*' # Push events to matching v*, i.e. v1.0, v20.15.10
-
-name: Create Release
-
-jobs:
- build:
- name: Create Release
- runs-on: ubuntu-latest
- steps:
- - name: Checkout code
- uses: actions/checkout@master
- - name: Create Release for Tag
- id: release_tag
- uses: yyx990803/release-tag@master
- env:
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- with:
- tag_name: ${{ github.ref }}
- body: |
- Please refer to [CHANGELOG.md](https://github.com/vuejs/vue-next/blob/master/CHANGELOG.md) for details.
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
new file mode 100644
index 000000000..c260a728e
--- /dev/null
+++ b/.github/workflows/release.yml
@@ -0,0 +1,55 @@
+name: Release
+
+on:
+ push:
+ tags:
+ - 'v*' # Push events to matching v*, i.e. v1.0, v20.15.10
+
+jobs:
+ test:
+ uses: ./.github/workflows/test.yml
+
+ release:
+ # prevents this action from running on forks
+ if: github.repository == 'vuejs/core'
+ needs: [test]
+ runs-on: ubuntu-latest
+ permissions:
+ contents: write
+ id-token: write
+ # Use Release environment for deployment protection
+ environment: Release
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+
+ - name: Install pnpm
+ uses: pnpm/action-setup@v4
+
+ - name: Install Node.js
+ uses: actions/setup-node@v4
+ with:
+ node-version-file: '.node-version'
+ registry-url: 'https://registry.npmjs.org'
+ cache: 'pnpm'
+
+ - name: Install deps
+ run: pnpm install
+
+ - name: Build and publish
+ id: publish
+ run: |
+ pnpm release --publishOnly
+ env:
+ NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
+
+ - name: Create GitHub release
+ id: release_tag
+ uses: yyx990803/release-tag@master
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ with:
+ tag_name: ${{ github.ref }}
+ body: |
+ For stable releases, please refer to [CHANGELOG.md](https://github.com/vuejs/core/blob/main/CHANGELOG.md) for details.
+ For pre-releases, please refer to [CHANGELOG.md](https://github.com/vuejs/core/blob/minor/CHANGELOG.md) of the `minor` branch.
diff --git a/.github/workflows/size-data.yml b/.github/workflows/size-data.yml
new file mode 100644
index 000000000..7f8bf7b08
--- /dev/null
+++ b/.github/workflows/size-data.yml
@@ -0,0 +1,51 @@
+name: size data
+
+on:
+ push:
+ branches:
+ - main
+ - minor
+ pull_request:
+ branches:
+ - main
+ - minor
+
+permissions:
+ contents: read
+
+env:
+ PUPPETEER_SKIP_DOWNLOAD: 'true'
+
+jobs:
+ upload:
+ if: github.repository == 'vuejs/core'
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Install pnpm
+ uses: pnpm/action-setup@v4.0.0
+
+ - name: Install Node.js
+ uses: actions/setup-node@v4
+ with:
+ node-version-file: '.node-version'
+ cache: pnpm
+
+ - name: Install dependencies
+ run: pnpm install
+
+ - run: pnpm run size
+
+ - name: Save PR number & base branch
+ if: ${{github.event_name == 'pull_request'}}
+ run: |
+ echo ${{ github.event.number }} > ./temp/size/number.txt
+ echo ${{ github.base_ref }} > ./temp/size/base.txt
+
+ - name: Upload Size Data
+ uses: actions/upload-artifact@v4
+ with:
+ name: size-data
+ path: temp/size
diff --git a/.github/workflows/size-report.yml b/.github/workflows/size-report.yml
new file mode 100644
index 000000000..f9d2052b6
--- /dev/null
+++ b/.github/workflows/size-report.yml
@@ -0,0 +1,85 @@
+name: size report
+
+on:
+ workflow_run:
+ workflows: ['size data']
+ types:
+ - completed
+
+permissions:
+ contents: read
+ pull-requests: write
+ issues: write
+
+env:
+ PUPPETEER_SKIP_DOWNLOAD: 'true'
+
+jobs:
+ size-report:
+ runs-on: ubuntu-latest
+ if: >
+ github.repository == 'vuejs/core' &&
+ github.event.workflow_run.event == 'pull_request' &&
+ github.event.workflow_run.conclusion == 'success'
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Install pnpm
+ uses: pnpm/action-setup@v4.0.0
+
+ - name: Install Node.js
+ uses: actions/setup-node@v4
+ with:
+ node-version-file: '.node-version'
+ cache: pnpm
+
+ - name: Install dependencies
+ run: pnpm install
+
+ - name: Download Size Data
+ uses: dawidd6/action-download-artifact@v6
+ with:
+ name: size-data
+ run_id: ${{ github.event.workflow_run.id }}
+ path: temp/size
+
+ - name: Read PR Number
+ id: pr-number
+ uses: juliangruber/read-file-action@v1
+ with:
+ path: temp/size/number.txt
+
+ - name: Read base branch
+ id: pr-base
+ uses: juliangruber/read-file-action@v1
+ with:
+ path: temp/size/base.txt
+
+ - name: Download Previous Size Data
+ uses: dawidd6/action-download-artifact@v6
+ with:
+ branch: ${{ steps.pr-base.outputs.content }}
+ workflow: size-data.yml
+ event: push
+ name: size-data
+ path: temp/size-prev
+ if_no_artifact_found: warn
+
+ - name: Prepare report
+ run: node scripts/size-report.js > size-report.md
+
+ - name: Read Size Report
+ id: size-report
+ uses: juliangruber/read-file-action@v1
+ with:
+ path: ./size-report.md
+
+ - name: Create Comment
+ uses: actions-cool/maintain-one-comment@v3
+ with:
+ token: ${{ secrets.GITHUB_TOKEN }}
+ number: ${{ steps.pr-number.outputs.content }}
+ body: |
+ ${{ steps.size-report.outputs.content }}
+
+ body-include: ''
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
new file mode 100644
index 000000000..70dc82248
--- /dev/null
+++ b/.github/workflows/test.yml
@@ -0,0 +1,108 @@
+name: 'test'
+
+on: workflow_call
+
+permissions:
+ contents: read # to fetch code (actions/checkout)
+
+jobs:
+ unit-test:
+ runs-on: ubuntu-latest
+ env:
+ PUPPETEER_SKIP_DOWNLOAD: 'true'
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Install pnpm
+ uses: pnpm/action-setup@v4.0.0
+
+ - name: Install Node.js
+ uses: actions/setup-node@v4
+ with:
+ node-version-file: '.node-version'
+ cache: 'pnpm'
+
+ - run: pnpm install
+
+ - name: Run unit tests
+ run: pnpm run test-unit
+
+ unit-test-windows:
+ runs-on: windows-latest
+ env:
+ PUPPETEER_SKIP_DOWNLOAD: 'true'
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Install pnpm
+ uses: pnpm/action-setup@v4.0.0
+
+ - name: Install Node.js
+ uses: actions/setup-node@v4
+ with:
+ node-version-file: '.node-version'
+ cache: 'pnpm'
+
+ - run: pnpm install
+
+ - name: Run compiler unit tests
+ run: pnpm run test-unit compiler
+
+ - name: Run ssr unit tests
+ run: pnpm run test-unit server-renderer
+
+ e2e-test:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Setup cache for Chromium binary
+ uses: actions/cache@v4
+ with:
+ path: ~/.cache/puppeteer
+ key: chromium-${{ hashFiles('pnpm-lock.yaml') }}
+
+ - name: Install pnpm
+ uses: pnpm/action-setup@v4.0.0
+
+ - name: Install Node.js
+ uses: actions/setup-node@v4
+ with:
+ node-version-file: '.node-version'
+ cache: 'pnpm'
+
+ - run: pnpm install
+ - run: node node_modules/puppeteer/install.mjs
+
+ - name: Run e2e tests
+ run: pnpm run test-e2e
+
+ - name: verify treeshaking
+ run: node scripts/verify-treeshaking.js
+
+ lint-and-test-dts:
+ runs-on: ubuntu-latest
+ env:
+ PUPPETEER_SKIP_DOWNLOAD: 'true'
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Install pnpm
+ uses: pnpm/action-setup@v4.0.0
+
+ - name: Install Node.js
+ uses: actions/setup-node@v4
+ with:
+ node-version-file: '.node-version'
+ cache: 'pnpm'
+
+ - run: pnpm install
+
+ - name: Run eslint
+ run: pnpm run lint
+
+ - name: Run prettier
+ run: pnpm run format-check
+
+ - name: Run type declaration tests
+ run: pnpm run test-dts
diff --git a/.gitignore b/.gitignore
index 1487e3b7c..9dd21f59b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -7,3 +7,7 @@ explorations
TODOs.md
*.log
.idea
+.eslintcache
+dts-build/packages
+*.tsbuildinfo
+*.tgz
diff --git a/.node-version b/.node-version
new file mode 100644
index 000000000..209e3ef4b
--- /dev/null
+++ b/.node-version
@@ -0,0 +1 @@
+20
diff --git a/.prettierignore b/.prettierignore
new file mode 100644
index 000000000..ca3c40849
--- /dev/null
+++ b/.prettierignore
@@ -0,0 +1,3 @@
+dist
+pnpm-lock.yaml
+CHANGELOG*.md
diff --git a/.prettierrc b/.prettierrc
index ef93d9482..759232e7c 100644
--- a/.prettierrc
+++ b/.prettierrc
@@ -1,5 +1,5 @@
-semi: false
-singleQuote: true
-printWidth: 80
-trailingComma: 'none'
-arrowParens: 'avoid'
+{
+ "semi": false,
+ "singleQuote": true,
+ "arrowParens": "avoid"
+}
diff --git a/.vscode/extensions.json b/.vscode/extensions.json
new file mode 100644
index 000000000..91ebd5692
--- /dev/null
+++ b/.vscode/extensions.json
@@ -0,0 +1,3 @@
+{
+ "recommendations": ["vitest.explorer"]
+}
diff --git a/.vscode/launch.json b/.vscode/launch.json
index b63ffc79b..9fc03aa9b 100644
--- a/.vscode/launch.json
+++ b/.vscode/launch.json
@@ -5,24 +5,15 @@
"version": "0.2.0",
"configurations": [
{
- "name": "Jest",
"type": "node",
"request": "launch",
- "program": "${workspaceFolder}/node_modules/.bin/jest",
- "stopOnEntry": false,
- "args": ["${fileBasename}", "--runInBand", "--detectOpenHandles"],
- "cwd": "${workspaceFolder}",
- "preLaunchTask": null,
- "runtimeExecutable": null,
- "runtimeArgs": ["--nolazy"],
- "env": {
- "NODE_ENV": "development"
- },
- "console": "integratedTerminal",
- "sourceMaps": true,
- "windows": {
- "program": "${workspaceFolder}/node_modules/jest/bin/jest",
- }
+ "name": "Vitest - Debug Current Test File",
+ "autoAttachChildProcesses": true,
+ "skipFiles": ["
Sponsors & Backers
+
+Vue.js is an MIT-licensed open source project with its ongoing development made possible entirely by the support of the awesome sponsors and backers listed in this file. If you'd like to join them, please consider [ sponsoring Vue's development](https://vuejs.org/sponsor/).
+
+
+
+
Special Sponsor
+
+
+
+
+
+
+
+
+
{msg}
', { - delimiters: ['{', '}'] + delimiters: ['{', '}'], }) const element = ast.children[0] as ElementNode const interpolation = element.children[0] as InterpolationNode @@ -308,14 +320,14 @@ describe('compiler: parse', () => { loc: { start: { offset: 4, line: 1, column: 5 }, end: { offset: 7, line: 1, column: 8 }, - source: 'msg' - } + source: 'msg', + }, }, loc: { start: { offset: 3, line: 1, column: 4 }, end: { offset: 8, line: 1, column: 9 }, - source: '{msg}' - } + source: '{msg}', + }, }) }) }) @@ -331,8 +343,8 @@ describe('compiler: parse', () => { loc: { start: { offset: 0, line: 1, column: 1 }, end: { offset: 7, line: 1, column: 8 }, - source: '' - } + source: '', + }, }) }) @@ -346,8 +358,8 @@ describe('compiler: parse', () => { loc: { start: { offset: 0, line: 1, column: 1 }, end: { offset: 10, line: 1, column: 11 }, - source: '' - } + source: '', + }, }) }) @@ -362,8 +374,8 @@ describe('compiler: parse', () => { loc: { start: { offset: 0, line: 1, column: 1 }, end: { offset: 10, line: 1, column: 11 }, - source: '' - } + source: '', + }, }) expect(comment2).toStrictEqual({ type: NodeTypes.COMMENT, @@ -371,8 +383,8 @@ describe('compiler: parse', () => { loc: { start: { offset: 10, line: 1, column: 11 }, end: { offset: 20, line: 1, column: 21 }, - source: '' - } + source: '', + }, }) }) @@ -389,38 +401,38 @@ describe('compiler: parse', () => { const rawText = `` const astWithComments = baseParse(`${rawText}`, { - comments: true + comments: true, }) expect( - (astWithComments.children[0] as ElementNode).children + (astWithComments.children[0] as ElementNode).children, ).toMatchObject([ { type: NodeTypes.ELEMENT, - tag: 'p' + tag: 'p', }, { - type: NodeTypes.COMMENT + type: NodeTypes.COMMENT, }, { type: NodeTypes.ELEMENT, - tag: 'p' - } + tag: 'p', + }, ]) const astWithoutComments = baseParse(`
${rawText}`, { - comments: false + comments: false, }) expect( - (astWithoutComments.children[0] as ElementNode).children + (astWithoutComments.children[0] as ElementNode).children, ).toMatchObject([ { type: NodeTypes.ELEMENT, - tag: 'p' + tag: 'p', }, { type: NodeTypes.ELEMENT, - tag: 'p' - } + tag: 'p', + }, ]) }) }) @@ -437,7 +449,6 @@ describe('compiler: parse', () => { tagType: ElementTypes.ELEMENT, codegenNode: undefined, props: [], - isSelfClosing: false, children: [ { type: NodeTypes.TEXT, @@ -445,15 +456,15 @@ describe('compiler: parse', () => { loc: { start: { offset: 5, line: 1, column: 6 }, end: { offset: 10, line: 1, column: 11 }, - source: 'hello' - } - } + source: 'hello', + }, + }, ], loc: { start: { offset: 0, line: 1, column: 1 }, end: { offset: 16, line: 1, column: 17 }, - source: '
\n foo bar`, { - isPreTag: tag => tag === 'pre' + test('should remove leading newline character immediately following the pre element start tag', () => { + const ast = parse(`
\n foo bar`, { + isPreTag: tag => tag === 'pre', + isIgnoreNewlineTag: tag => tag === 'pre', }) expect(ast.children).toHaveLength(1) const preElement = ast.children[0] as ElementNode @@ -2007,30 +2377,29 @@ foo expect((preElement.children[0] as TextNode).content).toBe(` foo bar `) }) - it('should NOT remove leading newline character immediately following child-tag of pre element', () => { - const ast = baseParse(`
\n foo bar`, { - isPreTag: tag => tag === 'pre' + test('should NOT remove leading newline character immediately following child-tag of pre element', () => { + const ast = parse(`
\n foo bar`, { + isPreTag: tag => tag === 'pre', }) const preElement = ast.children[0] as ElementNode expect(preElement.children).toHaveLength(2) expect((preElement.children[1] as TextNode).content).toBe( - `\n foo bar ` + `\n foo bar `, ) }) - it('self-closing pre tag', () => { - const ast = baseParse(`\n foo bar`, { - isPreTag: tag => tag === 'pre' + test('self-closing pre tag', () => { + const ast = parse(`\n foo bar`, { + isPreTag: tag => tag === 'pre', }) const elementAfterPre = ast.children[1] as ElementNode - // should not affect the and condense its whitepsace inside + // should not affect the and condense its whitespace inside expect((elementAfterPre.children[0] as TextNode).content).toBe(` foo bar`) }) - it('should NOT condense whitespaces in RCDATA text mode', () => { - const ast = baseParse(``, { - getTextMode: ({ tag }) => - tag === 'textarea' ? TextModes.RCDATA : TextModes.DATA + test('should NOT condense whitespaces in RCDATA text mode', () => { + const ast = parse(``, { + parseMode: 'html', }) const preElement = ast.children[0] as ElementNode expect(preElement.children).toHaveLength(1) @@ -2042,15 +2411,15 @@ foo const parse = (content: string, options?: ParserOptions) => baseParse(content, { whitespace: 'preserve', - ...options + ...options, }) - it('should still remove whitespaces at start/end inside an element', () => { + test('should still remove whitespaces at start/end inside an element', () => { const ast = parse(`
{{ o }}
{{ o + 'foo' }}
{{ o }}
{{ o + 'foo' }}
foo |
+ show-it
`)
+ expect(code).toMatchSnapshot()
+ })
+
+ test('stringify v-text', () => {
+ const { code } = compileWithStringify(`
+
+ <span>show-it </span>
`)
+ expect(code).toMatchSnapshot()
+ })
+
+ test('stringify v-text with escape', () => {
+ const { code } = compileWithStringify(`
+
+ text1
`)
+ expect(code).toMatchSnapshot()
+ })
+
+ test('should work for `)
+ })
+
+ it('should not warn with select > hr', () => {
+ let err: CompilerError | undefined
+ compile(``, {
+ onWarn: e => (err = e),
+ })
+ expect(err).toBeUndefined()
+ })
+})
diff --git a/packages/compiler-dom/__tests__/transforms/warnTransitionChildren.spec.ts b/packages/compiler-dom/__tests__/transforms/warnTransitionChildren.spec.ts
deleted file mode 100644
index 3cccdfffc..000000000
--- a/packages/compiler-dom/__tests__/transforms/warnTransitionChildren.spec.ts
+++ /dev/null
@@ -1,158 +0,0 @@
-import { compile } from '../../src'
-
-describe('compiler warnings', () => {
- describe('Transition', () => {
- function checkWarning(
- template: string,
- shouldWarn: boolean,
- message = ` >()
+ `,
+ files,
+ )
+ expect(props).toStrictEqual({
+ foo: ['String'],
+ })
+ })
+ })
+
+ describe('external type imports', () => {
+ test('relative ts', () => {
+ const files = {
+ '/foo.ts': 'export type P = { foo: number }',
+ '/bar.d.ts':
+ 'type X = { bar: string }; export { X as Y };' +
+ // verify that we can parse syntax that is only valid in d.ts
+ 'export const baz: boolean',
+ }
+ const { props, deps } = resolve(
+ `
+ import { P } from './foo'
+ import { Y as PP } from './bar'
+ defineProps ()
+ `,
+ files,
+ )
+ expect(props).toStrictEqual({
+ foo: ['Number'],
+ bar: ['String'],
+ })
+ expect(deps && [...deps]).toStrictEqual(Object.keys(files))
+ })
+
+ // #10635
+ test('relative tsx', () => {
+ const files = {
+ '/foo.tsx': 'export type P = { foo: number }',
+ '/bar/index.tsx': 'export type PP = { bar: string }',
+ }
+ const { props, deps } = resolve(
+ `
+ import { P } from './foo'
+ import { PP } from './bar'
+ defineProps ()
+ `,
+ files,
+ )
+ expect(props).toStrictEqual({
+ foo: ['Number'],
+ bar: ['String'],
+ })
+ expect(deps && [...deps]).toStrictEqual(Object.keys(files))
+ })
+
+ test.runIf(process.platform === 'win32')('relative ts on Windows', () => {
+ const files = {
+ 'C:\\Test\\FolderA\\foo.ts': 'export type P = { foo: number }',
+ 'C:\\Test\\FolderA\\bar.d.ts':
+ 'type X = { bar: string }; export { X as Y };' +
+ // verify that we can parse syntax that is only valid in d.ts
+ 'export const baz: boolean',
+ 'C:\\Test\\FolderB\\buz.ts': 'export type Z = { buz: string }',
+ }
+ const { props, deps } = resolve(
+ `
+ import { P } from './foo'
+ import { Y as PP } from './bar'
+ import { Z as PPP } from '../FolderB/buz'
+ defineProps ()
+ `,
+ files,
+ {},
+ 'C:\\Test\\FolderA\\Test.vue',
+ )
+ expect(props).toStrictEqual({
+ foo: ['Number'],
+ bar: ['String'],
+ buz: ['String'],
+ })
+ expect(deps && [...deps].map(normalize)).toStrictEqual(
+ Object.keys(files).map(normalize),
+ )
+ })
+
+ // #8244
+ test('utility type in external file', () => {
+ const files = {
+ '/foo.ts': 'type A = { n?: number }; export type B = Required',
+ }
+ const { props } = resolve(
+ `
+ import { B } from './foo'
+ defineProps()
+ `,
+ files,
+ )
+ expect(props).toStrictEqual({
+ n: ['Number'],
+ })
+ })
+
+ test('relative vue', () => {
+ const files = {
+ '/foo.vue':
+ '',
+ '/bar.vue':
+ '',
+ }
+ const { props, deps } = resolve(
+ `
+ import { P } from './foo.vue'
+ import { P as PP } from './bar.vue'
+ defineProps ()
+ `,
+ files,
+ )
+ expect(props).toStrictEqual({
+ foo: ['Number'],
+ bar: ['String'],
+ })
+ expect(deps && [...deps]).toStrictEqual(Object.keys(files))
+ })
+
+ test('relative (chained)', () => {
+ const files = {
+ '/foo.ts': `import type { P as PP } from './nested/bar.vue'
+ export type P = { foo: number } & PP`,
+ '/nested/bar.vue':
+ '',
+ }
+ const { props, deps } = resolve(
+ `
+ import { P } from './foo'
+ defineProps ()
+ `,
+ files,
+ )
+ expect(props).toStrictEqual({
+ foo: ['Number'],
+ bar: ['String'],
+ })
+ expect(deps && [...deps]).toStrictEqual(Object.keys(files))
+ })
+
+ test('relative (chained, re-export)', () => {
+ const files = {
+ '/foo.ts': `export { P as PP } from './bar'`,
+ '/bar.ts': 'export type P = { bar: string }',
+ }
+ const { props, deps } = resolve(
+ `
+ import { PP as P } from './foo'
+ defineProps ()
+ `,
+ files,
+ )
+ expect(props).toStrictEqual({
+ bar: ['String'],
+ })
+ expect(deps && [...deps]).toStrictEqual(Object.keys(files))
+ })
+
+ test('relative (chained, export *)', () => {
+ const files = {
+ '/foo.ts': `export * from './bar'`,
+ '/bar.ts': 'export type P = { bar: string }',
+ }
+ const { props, deps } = resolve(
+ `
+ import { P } from './foo'
+ defineProps ()
+ `,
+ files,
+ )
+ expect(props).toStrictEqual({
+ bar: ['String'],
+ })
+ expect(deps && [...deps]).toStrictEqual(Object.keys(files))
+ })
+
+ test('relative (default export)', () => {
+ const files = {
+ '/foo.ts': `export default interface P { foo: string }`,
+ '/bar.ts': `type X = { bar: string }; export default X`,
+ }
+ const { props, deps } = resolve(
+ `
+ import P from './foo'
+ import X from './bar'
+ defineProps ()
+ `,
+ files,
+ )
+ expect(props).toStrictEqual({
+ foo: ['String'],
+ bar: ['String'],
+ })
+ expect(deps && [...deps]).toStrictEqual(Object.keys(files))
+ })
+
+ test('relative (default re-export)', () => {
+ const files = {
+ '/bar.ts': `export { default } from './foo'`,
+ '/foo.ts': `export default interface P { foo: string }; export interface PP { bar: number }`,
+ '/baz.ts': `export { PP as default } from './foo'`,
+ }
+ const { props, deps } = resolve(
+ `
+ import P from './bar'
+ import PP from './baz'
+ defineProps ()
+ `,
+ files,
+ )
+ expect(props).toStrictEqual({
+ foo: ['String'],
+ bar: ['Number'],
+ })
+ expect(deps && [...deps]).toStrictEqual(Object.keys(files))
+ })
+
+ test('relative (re-export /w same source type name)', () => {
+ const files = {
+ '/foo.ts': `export default interface P { foo: string }`,
+ '/bar.ts': `export default interface PP { bar: number }`,
+ '/baz.ts': `export { default as X } from './foo'; export { default as XX } from './bar'; `,
+ }
+ const { props, deps } = resolve(
+ `import { X, XX } from './baz'
+ defineProps ()
+ `,
+ files,
+ )
+ expect(props).toStrictEqual({
+ foo: ['String'],
+ })
+ expect(deps && [...deps]).toStrictEqual(Object.keys(files))
+ })
+
+ test('ts module resolve', () => {
+ const files = {
+ '/node_modules/foo/package.json': JSON.stringify({
+ types: 'index.d.ts',
+ }),
+ '/node_modules/foo/index.d.ts': 'export type P = { foo: number }',
+ '/tsconfig.json': JSON.stringify({
+ compilerOptions: {
+ paths: {
+ bar: ['./pp.ts'],
+ },
+ },
+ }),
+ '/pp.ts': 'export type PP = { bar: string }',
+ }
+
+ const { props, deps } = resolve(
+ `
+ import { P } from 'foo'
+ import { PP } from 'bar'
+ defineProps ()
+ `,
+ files,
+ )
+
+ expect(props).toStrictEqual({
+ foo: ['Number'],
+ bar: ['String'],
+ })
+ expect(deps && [...deps]).toStrictEqual([
+ '/node_modules/foo/index.d.ts',
+ '/pp.ts',
+ ])
+ })
+
+ test('ts module resolve w/ project reference & extends', () => {
+ const files = {
+ '/tsconfig.json': JSON.stringify({
+ references: [
+ {
+ path: './tsconfig.app.json',
+ },
+ ],
+ }),
+ '/tsconfig.app.json': JSON.stringify({
+ include: ['**/*.ts', '**/*.vue'],
+ extends: './tsconfig.web.json',
+ }),
+ '/tsconfig.web.json': JSON.stringify({
+ compilerOptions: {
+ composite: true,
+ paths: {
+ bar: ['./user.ts'],
+ },
+ },
+ }),
+ '/user.ts': 'export type User = { bar: string }',
+ }
+
+ const { props, deps } = resolve(
+ `
+ import { User } from 'bar'
+ defineProps ()
+ `,
+ files,
+ )
+
+ expect(props).toStrictEqual({
+ bar: ['String'],
+ })
+ expect(deps && [...deps]).toStrictEqual(['/src/Foo.vue'])
+ })
+
+ test('global types', () => {
+ const files = {
+ // ambient
+ '/app.d.ts':
+ 'declare namespace App { interface User { name: string } }',
+ // module - should only respect the declare global block
+ '/global.d.ts': `
+ declare type PP = { bar: number }
+ declare global {
+ type PP = { bar: string }
+ }
+ export {}
+ `,
+ }
+
+ const { props, deps } = resolve(`defineProps The next line is empty. This is the last line. {{ render }} {{ foobar }} {{ foobar }} {{ foobar }} {{ foobar }} {{ foobar }} {{ foobar }} vuejs/vue@{{ currentBranch }} vuejs/core@{{ currentBranch }} (You can double click on an item to turn it into a folder.) (You can double click on an item to turn it into a folder.)()
+ `).props,
+ ).toStrictEqual({
+ foo: ['Number'],
+ })
+ })
+
+ test('indexed access type (advanced)', () => {
+ expect(
+ resolve(`
+ type K = 'foo' | 'bar'
+ type T = { foo: string, bar: number }
+ type S = { foo: { foo: T[string] }, bar: { bar: string } }
+ defineProps()
+ `).props,
+ ).toStrictEqual({
+ foo: ['String', 'Number'],
+ bar: ['String'],
+ })
+ })
+
+ test('indexed access type (number)', () => {
+ expect(
+ resolve(`
+ type A = (string | number)[]
+ type AA = ArrayThe next line contains four spaces.
`,
- ssr: true
+ ssr: true,
})
expect(code).toMatch(`_ssrRenderAttr(\"src\", _imports_1)`)
expect(code).toMatch(`_createVNode(\"img\", { src: _imports_1 })`)
})
+
+// #3874
+test('should not hoist srcset URLs in SSR mode', () => {
+ const { code } = compile({
+ filename: 'example.vue',
+ source: `
+
+
+
-4 |
-6 | "
+exports[`compiler: codeframe > invalid start and end 1`] = `
+"1 | "
`;
-exports[`compiler: codeframe line near bottom 1`] = `
-"4 |
+6 |
| ^^^^^^^^^
7 |
+ | ^^^^^^
+4 |
-6 |
+ | ^^^^^^^
+6 |
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+7 |
+4 |
+6 | "
+`;
+
+exports[`compiler: codeframe > line near bottom 1`] = `
+"4 |
-4 |
{{ { mixinData, selfData } }}
`,
mixins: [{ data: () => ({ mixinData: 'mixinData' }) }],
- data: () => ({ selfData: 'selfData' })
+ data: () => ({ selfData: 'selfData' }),
})
const vm = new App().$mount()
expect(vm.$el.textContent).toBe(
JSON.stringify(
{
mixinData: 'mixinData',
- selfData: 'selfData'
+ selfData: 'selfData',
},
null,
- 2
- )
+ 2,
+ ),
)
})
test('beforeDestroy/destroyed', async () => {
- const beforeDestroy = jest.fn()
- const destroyed = jest.fn()
+ const beforeDestroy = vi.fn()
+ const destroyed = vi.fn()
const child = {
template: `foo`,
beforeDestroy,
- destroyed
+ destroyed,
}
const vm = new Vue({
@@ -98,7 +98,7 @@ test('beforeDestroy/destroyed', async () => {
data() {
return { ok: true }
},
- components: { child }
+ components: { child },
}).$mount() as any
vm.ok = false
@@ -107,22 +107,22 @@ test('beforeDestroy/destroyed', async () => {
expect(destroyed).toHaveBeenCalled()
expect(
- deprecationData[DeprecationTypes.OPTIONS_BEFORE_DESTROY].message
+ deprecationData[DeprecationTypes.OPTIONS_BEFORE_DESTROY].message,
).toHaveBeenWarned()
expect(
- deprecationData[DeprecationTypes.OPTIONS_DESTROYED].message
+ deprecationData[DeprecationTypes.OPTIONS_DESTROYED].message,
).toHaveBeenWarned()
})
test('beforeDestroy/destroyed in Vue.extend components', async () => {
- const beforeDestroy = jest.fn()
- const destroyed = jest.fn()
+ const beforeDestroy = vi.fn()
+ const destroyed = vi.fn()
const child = Vue.extend({
template: `foo`,
beforeDestroy,
- destroyed
+ destroyed,
})
const vm = new Vue({
@@ -130,7 +130,7 @@ test('beforeDestroy/destroyed in Vue.extend components', async () => {
data() {
return { ok: true }
},
- components: { child }
+ components: { child },
}).$mount() as any
vm.ok = false
@@ -139,10 +139,10 @@ test('beforeDestroy/destroyed in Vue.extend components', async () => {
expect(destroyed).toHaveBeenCalled()
expect(
- deprecationData[DeprecationTypes.OPTIONS_BEFORE_DESTROY].message
+ deprecationData[DeprecationTypes.OPTIONS_BEFORE_DESTROY].message,
).toHaveBeenWarned()
expect(
- deprecationData[DeprecationTypes.OPTIONS_DESTROYED].message
+ deprecationData[DeprecationTypes.OPTIONS_DESTROYED].message,
).toHaveBeenWarned()
})
diff --git a/packages/vue-compat/__tests__/refInfor.spec.ts b/packages/vue-compat/__tests__/refInfor.spec.ts
deleted file mode 100644
index af1a78af5..000000000
--- a/packages/vue-compat/__tests__/refInfor.spec.ts
+++ /dev/null
@@ -1,57 +0,0 @@
-import Vue from '@vue/compat'
-import { nextTick } from '../../runtime-core/src/scheduler'
-import {
- DeprecationTypes,
- deprecationData,
- toggleDeprecationWarning
-} from '../../runtime-core/src/compat/compatConfig'
-
-beforeEach(() => {
- toggleDeprecationWarning(true)
- Vue.configureCompat({
- MODE: 2,
- GLOBAL_MOUNT: 'suppress-warning'
- })
-})
-
-afterEach(() => {
- toggleDeprecationWarning(false)
- Vue.configureCompat({ MODE: 3 })
-})
-
-test('V_FOR_REF', async () => {
- const vm = new Vue({
- data() {
- return {
- ok: true,
- list: [1, 2, 3]
- }
- },
- template: `
-
- hello
\n')
+ expect(await html('#editor div')).toBe('hello
\n')
await page().type('textarea', '\n## foo\n\n- bar\n- baz')
@@ -27,9 +23,9 @@ describe('e2e: markdown', () => {
await expectByPolling(
() => html('#editor div'),
- 'hello
\n' +
- 'foo
\n' +
- '\n
\n'
+ 'hello
\n' +
+ 'foo
\n' +
+ '\n
\n',
)
}
@@ -38,7 +34,7 @@ describe('e2e: markdown', () => {
async () => {
await testMarkdown('classic')
},
- E2E_TIMEOUT
+ E2E_TIMEOUT,
)
test(
@@ -46,6 +42,6 @@ describe('e2e: markdown', () => {
async () => {
await testMarkdown('composition')
},
- E2E_TIMEOUT
+ E2E_TIMEOUT,
)
})
diff --git a/packages/vue/__tests__/e2e/ssr-custom-element.spec.ts b/packages/vue/__tests__/e2e/ssr-custom-element.spec.ts
new file mode 100644
index 000000000..c875f1bee
--- /dev/null
+++ b/packages/vue/__tests__/e2e/ssr-custom-element.spec.ts
@@ -0,0 +1,174 @@
+import path from 'node:path'
+import { setupPuppeteer } from './e2eUtils'
+
+const { page, click, text } = setupPuppeteer()
+
+beforeEach(async () => {
+ await page().addScriptTag({
+ path: path.resolve(__dirname, '../../dist/vue.global.js'),
+ })
+})
+
+async function setContent(html: string) {
+ await page().setContent(`
+
+ `,
+ data() {
+ return {
+ first: true,
+ second: false,
+ }
+ },
+ methods: {
+ secondClick(this: any) {
+ this.first = false
+ },
+ },
+ }).mount('#app')
+ })
+
+ expect(await isChecked('#first')).toBe(true)
+ expect(await isChecked('#second')).toBe(false)
+ await click('#second')
+ await nextTick()
+ expect(await isChecked('#first')).toBe(false)
+ expect(await isChecked('#second')).toBe(true)
+})
diff --git a/packages/vue/__tests__/e2eUtils.ts b/packages/vue/__tests__/e2eUtils.ts
deleted file mode 100644
index 9ae3cc7fd..000000000
--- a/packages/vue/__tests__/e2eUtils.ts
+++ /dev/null
@@ -1,175 +0,0 @@
-import puppeteer from 'puppeteer'
-
-export const E2E_TIMEOUT = 30 * 1000
-
-const puppeteerOptions = process.env.CI
- ? { args: ['--no-sandbox', '--disable-setuid-sandbox'] }
- : {}
-
-const maxTries = 30
-export const timeout = (n: number) => new Promise(r => setTimeout(r, n))
-
-export async function expectByPolling(
- poll: () => PromiseLatest Vue.js Commits
-
+ v-model="currentBranch"
+ />
-
- by
+ {{ sha.slice(0, 7) }}
+ -
+ by
+
at {{ formatDate(commit.author.date) }}
{{ stats }}
todos
-
+
-
{{ stats }}
todos
-
+
-