├── packages ├── vue │ ├── index.mjs │ ├── compiler-sfc │ │ ├── index.mjs │ │ ├── index.d.ts │ │ ├── index.js │ │ └── package.json │ ├── server-renderer │ │ ├── index.mjs │ │ ├── index.d.ts │ │ ├── index.js │ │ └── package.json │ ├── ref-macros.d.ts │ ├── index.js │ ├── api-extractor.json │ ├── src │ │ ├── dev.ts │ │ └── runtime.ts │ ├── macros-global.d.ts │ ├── LICENSE │ ├── __tests__ │ │ └── transition.html │ └── examples │ │ ├── classic │ │ └── markdown.html │ │ ├── __tests__ │ │ └── markdown.spec.ts │ │ └── composition │ │ └── markdown.html ├── compiler-dom │ ├── README.md │ ├── index.js │ ├── api-extractor.json │ ├── __tests__ │ │ ├── index.spec.ts │ │ ├── transforms │ │ │ ├── __snapshots__ │ │ │ │ ├── vShow.spec.ts.snap │ │ │ │ ├── warnTransitionChildren.spec.ts.snap │ │ │ │ └── stringifyStatic.spec.ts.snap │ │ │ ├── ignoreSideEffectTags.spec.ts │ │ │ └── vShow.spec.ts │ │ ├── __snapshots__ │ │ │ └── index.spec.ts.snap │ │ └── decoderHtmlBrowser.spec.ts │ ├── src │ │ ├── decodeHtmlBrowser.ts │ │ ├── transforms │ │ │ ├── vShow.ts │ │ │ ├── ignoreSideEffectTags.ts │ │ │ ├── vHtml.ts │ │ │ ├── vText.ts │ │ │ ├── transformStyle.ts │ │ │ └── warnTransitionChildren.ts │ │ └── runtimeHelpers.ts │ ├── LICENSE │ └── package.json ├── compiler-ssr │ ├── README.md │ ├── api-extractor.json │ ├── __tests__ │ │ ├── utils.ts │ │ ├── ssrPortal.spec.ts │ │ └── ssrSuspense.spec.ts │ ├── package.json │ ├── src │ │ ├── errors.ts │ │ └── transforms │ │ │ ├── ssrVShow.ts │ │ │ ├── ssrTransformTransitionGroup.ts │ │ │ ├── ssrVFor.ts │ │ │ ├── ssrInjectCssVars.ts │ │ │ ├── ssrInjectFallthroughAttrs.ts │ │ │ └── ssrTransformSlotOutlet.ts │ └── LICENSE ├── compiler-core │ ├── README.md │ ├── src │ │ └── transforms │ │ │ ├── noopDirectiveTransform.ts │ │ │ ├── vOnce.ts │ │ │ └── vMemo.ts │ ├── index.js │ ├── api-extractor.json │ ├── __tests__ │ │ └── transforms │ │ │ ├── noopDirectiveTransform.spec.ts │ │ │ ├── __snapshots__ │ │ │ └── transformExpressions.spec.ts.snap │ │ │ └── vMemo.spec.ts │ ├── package.json │ └── LICENSE ├── compiler-sfc │ ├── __tests__ │ │ ├── fixture │ │ │ └── import.scss │ │ ├── __snapshots__ │ │ │ └── compileTemplate.spec.ts.snap │ │ └── utils.ts │ ├── src │ │ ├── shims.d.ts │ │ ├── cache.ts │ │ ├── warn.ts │ │ ├── stylePluginTrim.ts │ │ ├── templateUtils.ts │ │ └── index.ts │ ├── api-extractor.json │ ├── LICENSE │ └── package.json ├── reactivity-transform │ ├── src │ │ ├── index.ts │ │ └── babelPlugin.ts │ ├── api-extractor.json │ └── package.json ├── sfc-playground │ ├── src │ │ ├── vue-dev-proxy.ts │ │ ├── download │ │ │ ├── template │ │ │ │ ├── main.js │ │ │ │ ├── vite.config.js │ │ │ │ ├── README.md │ │ │ │ ├── package.json │ │ │ │ └── index.html │ │ │ └── download.ts │ │ ├── main.ts │ │ ├── icons │ │ │ ├── Share.vue │ │ │ ├── Moon.vue │ │ │ ├── GitHub.vue │ │ │ ├── Download.vue │ │ │ └── Sun.vue │ │ └── App.vue │ ├── public │ │ └── logo.svg │ ├── package.json │ ├── index.html │ └── vite.config.ts ├── shared │ ├── README.md │ ├── index.js │ ├── api-extractor.json │ ├── src │ │ ├── typeUtils.ts │ │ ├── globalsWhitelist.ts │ │ ├── shapeFlags.ts │ │ ├── makeMap.ts │ │ ├── slotFlags.ts │ │ ├── toDisplayString.ts │ │ ├── escapeHtml.ts │ │ └── looseEqual.ts │ ├── __tests__ │ │ ├── escapeHtml.spec.ts │ │ └── normalizeProp.spec.ts │ ├── package.json │ └── LICENSE ├── reactivity │ ├── src │ │ ├── warning.ts │ │ ├── operations.ts │ │ ├── index.ts │ │ └── dep.ts │ ├── index.js │ ├── api-extractor.json │ ├── package.json │ ├── README.md │ └── LICENSE ├── size-check │ ├── package.json │ ├── README.md │ ├── src │ │ └── index.ts │ ├── vite.config.js │ └── brotli.js ├── vue-compat │ ├── src │ │ ├── esm-index.ts │ │ ├── esm-runtime.ts │ │ ├── dev.ts │ │ ├── runtime.ts │ │ └── createCompatVue.ts │ ├── index.js │ ├── __tests__ │ │ └── utils.ts │ ├── package.json │ └── LICENSE ├── server-renderer │ ├── src │ │ ├── helpers │ │ │ ├── ssrInterpolate.ts │ │ │ ├── ssrRenderSuspense.ts │ │ │ ├── ssrRenderComponent.ts │ │ │ ├── ssrGetDirectiveProps.ts │ │ │ ├── ssrRenderList.ts │ │ │ ├── ssrRenderTeleport.ts │ │ │ └── ssrVModelHelpers.ts │ │ └── index.ts │ ├── index.js │ ├── api-extractor.json │ ├── __tests__ │ │ ├── ssrInterpolate.spec.ts │ │ ├── webStream.spec.ts │ │ ├── ssrComputed.spec.ts │ │ └── ssrDynamicComponent.spec.ts │ ├── package.json │ └── LICENSE ├── runtime-core │ ├── index.js │ ├── api-extractor.json │ ├── src │ │ ├── apiComputed.ts │ │ ├── helpers │ │ │ ├── toHandlers.ts │ │ │ ├── useSsrContext.ts │ │ │ ├── withMemo.ts │ │ │ └── createSlots.ts │ │ ├── compat │ │ │ ├── data.ts │ │ │ ├── instanceListeners.ts │ │ │ ├── attrsFallthrough.ts │ │ │ ├── instanceChildren.ts │ │ │ ├── props.ts │ │ │ ├── component.ts │ │ │ ├── customDirective.ts │ │ │ └── componentAsync.ts │ │ ├── featureFlags.ts │ │ └── profiling.ts │ ├── types │ │ ├── refBail.d.ts │ │ └── scriptSetupHelpers.d.ts │ ├── __tests__ │ │ ├── misc.spec.ts │ │ ├── helpers │ │ │ ├── toHandlers.spec.ts │ │ │ ├── createSlots.spec.ts │ │ │ └── renderList.spec.ts │ │ └── rendererElement.spec.ts │ ├── README.md │ ├── package.json │ └── LICENSE ├── runtime-dom │ ├── index.js │ ├── types │ │ └── refBail.d.ts │ ├── api-extractor.json │ ├── README.md │ ├── __tests__ │ │ ├── directives │ │ │ └── vCloak.spec.ts │ │ ├── createApp.spec.ts │ │ ├── customizedBuiltIn.spec.ts │ │ ├── patchClass.spec.ts │ │ └── helpers │ │ │ └── useCssModule.spec.ts │ ├── src │ │ ├── modules │ │ │ └── class.ts │ │ ├── helpers │ │ │ └── useCssModule.ts │ │ └── directives │ │ │ └── vShow.ts │ ├── package.json │ └── LICENSE ├── runtime-test │ ├── index.js │ ├── api-extractor.json │ ├── src │ │ ├── triggerEvent.ts │ │ ├── patchProp.ts │ │ ├── index.ts │ │ └── serialize.ts │ ├── README.md │ ├── package.json │ └── LICENSE ├── template-explorer │ ├── package.json │ ├── README.md │ ├── local.html │ ├── index.html │ └── style.css └── global.d.ts ├── pnpm-workspace.yaml ├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ └── config.yml ├── workflows │ ├── release-tag.yml │ └── ci.yml └── dependabot.yml ├── .prettierrc ├── netlify.toml ├── .gitignore ├── test-dts ├── tsconfig.json ├── tsconfig.build.json ├── README.md ├── inject.test-d.ts ├── index.d.ts ├── reactivity.test-d.ts ├── componentTypeExtensions.test-d.tsx ├── functionalComponent.test-d.tsx └── tsx.test-d.tsx ├── scripts ├── filter-unit.js ├── preinstall.js ├── filter-e2e.js ├── verifyCommit.js └── utils.js ├── BACKERS.md ├── .vscode ├── settings.json └── launch.json ├── SECURITY.md ├── README.md ├── tsconfig.json ├── LICENSE └── api-extractor.json /packages/vue/index.mjs: -------------------------------------------------------------------------------- 1 | export * from './index.js' -------------------------------------------------------------------------------- /packages/compiler-dom/README.md: -------------------------------------------------------------------------------- 1 | # @vue/compiler-dom -------------------------------------------------------------------------------- /packages/compiler-ssr/README.md: -------------------------------------------------------------------------------- 1 | # @vue/compiler-ssr -------------------------------------------------------------------------------- /packages/compiler-core/README.md: -------------------------------------------------------------------------------- 1 | # @vue/compiler-core 2 | -------------------------------------------------------------------------------- /pnpm-workspace.yaml: -------------------------------------------------------------------------------- 1 | packages: 2 | - 'packages/*' 3 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: yyx990803 2 | open_collective: vuejs 3 | -------------------------------------------------------------------------------- /packages/vue/compiler-sfc/index.mjs: -------------------------------------------------------------------------------- 1 | export * from '@vue/compiler-sfc' -------------------------------------------------------------------------------- /packages/vue/compiler-sfc/index.d.ts: -------------------------------------------------------------------------------- 1 | export * from '@vue/compiler-sfc' 2 | -------------------------------------------------------------------------------- /packages/vue/server-renderer/index.mjs: -------------------------------------------------------------------------------- 1 | export * from '@vue/server-renderer' -------------------------------------------------------------------------------- /packages/vue/server-renderer/index.d.ts: -------------------------------------------------------------------------------- 1 | export * from '@vue/server-renderer' 2 | -------------------------------------------------------------------------------- /packages/compiler-sfc/__tests__/fixture/import.scss: -------------------------------------------------------------------------------- 1 | div { 2 | color: red; 3 | } 4 | -------------------------------------------------------------------------------- /packages/reactivity-transform/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './reactivityTransform' 2 | -------------------------------------------------------------------------------- /packages/vue/compiler-sfc/index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('@vue/compiler-sfc') 2 | -------------------------------------------------------------------------------- /packages/vue/server-renderer/index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('@vue/server-renderer') 2 | -------------------------------------------------------------------------------- /packages/reactivity-transform/src/babelPlugin.ts: -------------------------------------------------------------------------------- 1 | export function plugin() { 2 | // TODO 3 | } 4 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | semi: false 2 | singleQuote: true 3 | printWidth: 80 4 | trailingComma: 'none' 5 | arrowParens: 'avoid' 6 | -------------------------------------------------------------------------------- /netlify.toml: -------------------------------------------------------------------------------- 1 | [build.environment] 2 | NODE_VERSION = "16" 3 | NPM_FLAGS = "--version" # prevent Netlify npm install 4 | -------------------------------------------------------------------------------- /packages/sfc-playground/src/vue-dev-proxy.ts: -------------------------------------------------------------------------------- 1 | // serve vue to the iframe sandbox during dev. 2 | export * from 'vue' 3 | -------------------------------------------------------------------------------- /packages/shared/README.md: -------------------------------------------------------------------------------- 1 | # @vue/shared 2 | 3 | Internal utility functions and constants shared across `@vue` packages. 4 | -------------------------------------------------------------------------------- /packages/vue/compiler-sfc/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "main": "index.js", 3 | "module": "index.mjs", 4 | "types": "index.d.ts" 5 | } -------------------------------------------------------------------------------- /packages/vue/ref-macros.d.ts: -------------------------------------------------------------------------------- 1 | // TODO deprecated file - to be removed when out of experimental 2 | import './macros-global' 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | dist 2 | .DS_Store 3 | node_modules 4 | coverage 5 | temp 6 | explorations 7 | TODOs.md 8 | *.log 9 | .idea 10 | -------------------------------------------------------------------------------- /packages/vue/server-renderer/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "main": "index.js", 3 | "module": "index.mjs", 4 | "types": "index.d.ts" 5 | } -------------------------------------------------------------------------------- /packages/reactivity/src/warning.ts: -------------------------------------------------------------------------------- 1 | export function warn(msg: string, ...args: any[]) { 2 | console.warn(`[Vue warn] ${msg}`, ...args) 3 | } 4 | -------------------------------------------------------------------------------- /packages/compiler-sfc/src/shims.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'merge-source-map' { 2 | export default function merge(oldMap: object, newMap: object): object 3 | } 4 | -------------------------------------------------------------------------------- /packages/sfc-playground/src/download/template/main.js: -------------------------------------------------------------------------------- 1 | import { createApp } from 'vue' 2 | import App from './App.vue' 3 | 4 | createApp(App).mount('#app') 5 | -------------------------------------------------------------------------------- /packages/size-check/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@vue/size-check", 3 | "version": "3.2.31", 4 | "private": true, 5 | "scripts": { 6 | "build": "vite build" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /packages/vue-compat/src/esm-index.ts: -------------------------------------------------------------------------------- 1 | import Vue from './index' 2 | 3 | export default Vue 4 | export * from '@vue/runtime-dom' 5 | 6 | const { configureCompat } = Vue 7 | export { configureCompat } 8 | -------------------------------------------------------------------------------- /packages/compiler-core/src/transforms/noopDirectiveTransform.ts: -------------------------------------------------------------------------------- 1 | import { DirectiveTransform } from '../transform' 2 | 3 | export const noopDirectiveTransform: DirectiveTransform = () => ({ props: [] }) 4 | -------------------------------------------------------------------------------- /packages/vue-compat/src/esm-runtime.ts: -------------------------------------------------------------------------------- 1 | import Vue from './runtime' 2 | 3 | export default Vue 4 | export * from '@vue/runtime-dom' 5 | 6 | const { configureCompat } = Vue 7 | export { configureCompat } 8 | -------------------------------------------------------------------------------- /packages/vue/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | if (process.env.NODE_ENV === 'production') { 4 | module.exports = require('./dist/vue.cjs.prod.js') 5 | } else { 6 | module.exports = require('./dist/vue.cjs.js') 7 | } 8 | -------------------------------------------------------------------------------- /packages/size-check/README.md: -------------------------------------------------------------------------------- 1 | # Size Check 2 | 3 | This package is private and is used for checking the baseline runtime size after tree-shaking (with only the bare minimal code required to render something to the screen). 4 | -------------------------------------------------------------------------------- /packages/vue-compat/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | if (process.env.NODE_ENV === 'production') { 4 | module.exports = require('./dist/vue.cjs.prod.js') 5 | } else { 6 | module.exports = require('./dist/vue.cjs.js') 7 | } 8 | -------------------------------------------------------------------------------- /packages/shared/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | if (process.env.NODE_ENV === 'production') { 4 | module.exports = require('./dist/shared.cjs.prod.js') 5 | } else { 6 | module.exports = require('./dist/shared.cjs.js') 7 | } 8 | -------------------------------------------------------------------------------- /packages/server-renderer/src/helpers/ssrInterpolate.ts: -------------------------------------------------------------------------------- 1 | import { escapeHtml, toDisplayString } from '@vue/shared' 2 | 3 | export function ssrInterpolate(value: unknown): string { 4 | return escapeHtml(toDisplayString(value)) 5 | } 6 | -------------------------------------------------------------------------------- /test-dts/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "noEmit": true, 5 | "declaration": true 6 | }, 7 | "exclude": ["../packages/*/__tests__", "../packages/template-explorer"] 8 | } 9 | -------------------------------------------------------------------------------- /packages/reactivity/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | if (process.env.NODE_ENV === 'production') { 4 | module.exports = require('./dist/reactivity.cjs.prod.js') 5 | } else { 6 | module.exports = require('./dist/reactivity.cjs.js') 7 | } 8 | -------------------------------------------------------------------------------- /packages/compiler-dom/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | if (process.env.NODE_ENV === 'production') { 4 | module.exports = require('./dist/compiler-dom.cjs.prod.js') 5 | } else { 6 | module.exports = require('./dist/compiler-dom.cjs.js') 7 | } 8 | -------------------------------------------------------------------------------- /packages/runtime-core/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | if (process.env.NODE_ENV === 'production') { 4 | module.exports = require('./dist/runtime-core.cjs.prod.js') 5 | } else { 6 | module.exports = require('./dist/runtime-core.cjs.js') 7 | } 8 | -------------------------------------------------------------------------------- /packages/runtime-dom/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | if (process.env.NODE_ENV === 'production') { 4 | module.exports = require('./dist/runtime-dom.cjs.prod.js') 5 | } else { 6 | module.exports = require('./dist/runtime-dom.cjs.js') 7 | } 8 | -------------------------------------------------------------------------------- /packages/runtime-test/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | if (process.env.NODE_ENV === 'production') { 4 | module.exports = require('./dist/runtime-test.cjs.prod.js') 5 | } else { 6 | module.exports = require('./dist/runtime-test.cjs.js') 7 | } 8 | -------------------------------------------------------------------------------- /packages/sfc-playground/src/download/template/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import vue from '@vitejs/plugin-vue' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [vue()] 7 | }) 8 | -------------------------------------------------------------------------------- /packages/size-check/src/index.ts: -------------------------------------------------------------------------------- 1 | import { h, createApp } from '@vue/runtime-dom' 2 | 3 | // The bare minimum code required for rendering something to the screen 4 | createApp({ 5 | render: () => h('div', 'hello world!') 6 | }).mount('#app') 7 | -------------------------------------------------------------------------------- /packages/compiler-core/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | if (process.env.NODE_ENV === 'production') { 4 | module.exports = require('./dist/compiler-core.cjs.prod.js') 5 | } else { 6 | module.exports = require('./dist/compiler-core.cjs.js') 7 | } 8 | -------------------------------------------------------------------------------- /packages/compiler-sfc/src/cache.ts: -------------------------------------------------------------------------------- 1 | import LRU from 'lru-cache' 2 | 3 | export function createCache(size = 500) { 4 | return __GLOBAL__ || __ESM_BROWSER__ 5 | ? new Map() 6 | : (new LRU(size) as any as Map) 7 | } 8 | -------------------------------------------------------------------------------- /packages/server-renderer/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | if (process.env.NODE_ENV === 'production') { 4 | module.exports = require('./dist/server-renderer.cjs.prod.js') 5 | } else { 6 | module.exports = require('./dist/server-renderer.cjs.js') 7 | } 8 | -------------------------------------------------------------------------------- /packages/size-check/vite.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | build: { 3 | rollupOptions: { 4 | input: ['src/index.ts'], 5 | output: { 6 | entryFileNames: `[name].js` 7 | } 8 | }, 9 | minify: 'terser' 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /scripts/filter-unit.js: -------------------------------------------------------------------------------- 1 | const { e2eTests } = require('./filter-e2e') 2 | 3 | module.exports = list => { 4 | return { 5 | filtered: list 6 | .filter(t => !e2eTests.some(tt => t.includes(tt))) 7 | .map(test => ({ test })) 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /scripts/preinstall.js: -------------------------------------------------------------------------------- 1 | if (!/pnpm/.test(process.env.npm_execpath || '')) { 2 | console.warn( 3 | `\u001b[33mThis repository requires using pnpm as the package manager ` + 4 | ` for scripts to work properly.\u001b[39m\n` 5 | ) 6 | process.exit(1) 7 | } 8 | -------------------------------------------------------------------------------- /packages/shared/api-extractor.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../api-extractor.json", 3 | "mainEntryPointFilePath": "./dist/packages//src/index.d.ts", 4 | "dtsRollup": { 5 | "publicTrimmedFilePath": "./dist/.d.ts" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /packages/compiler-dom/api-extractor.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../api-extractor.json", 3 | "mainEntryPointFilePath": "./dist/packages//src/index.d.ts", 4 | "dtsRollup": { 5 | "publicTrimmedFilePath": "./dist/.d.ts" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /packages/compiler-sfc/api-extractor.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../api-extractor.json", 3 | "mainEntryPointFilePath": "./dist/packages//src/index.d.ts", 4 | "dtsRollup": { 5 | "publicTrimmedFilePath": "./dist/.d.ts" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /packages/compiler-ssr/api-extractor.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../api-extractor.json", 3 | "mainEntryPointFilePath": "./dist/packages//src/index.d.ts", 4 | "dtsRollup": { 5 | "publicTrimmedFilePath": "./dist/.d.ts" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /packages/runtime-dom/types/refBail.d.ts: -------------------------------------------------------------------------------- 1 | // Note: this file is auto concatenated to the end of the bundled d.ts during 2 | // build. 3 | 4 | declare module '@vue/reactivity' { 5 | export interface RefUnwrapBailTypes { 6 | runtimeDOMBailTypes: Node | Window 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /packages/vue/api-extractor.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../api-extractor.json", 3 | "mainEntryPointFilePath": "./dist/packages//src/index.d.ts", 4 | "dtsRollup": { 5 | "publicTrimmedFilePath": "./dist/.d.ts" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /packages/reactivity-transform/api-extractor.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../api-extractor.json", 3 | "mainEntryPointFilePath": "./dist/packages//src/index.d.ts", 4 | "dtsRollup": { 5 | "publicTrimmedFilePath": "./dist/.d.ts" 6 | } 7 | } -------------------------------------------------------------------------------- /packages/reactivity/api-extractor.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../api-extractor.json", 3 | "mainEntryPointFilePath": "./dist/packages//src/index.d.ts", 4 | "dtsRollup": { 5 | "publicTrimmedFilePath": "./dist/.d.ts" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /test-dts/tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "noEmit": true, 4 | "jsx": "preserve", 5 | "module": "esnext", 6 | "strict": true, 7 | "moduleResolution": "node", 8 | "lib": ["esnext", "dom"] 9 | }, 10 | "include": ["./*"] 11 | } 12 | -------------------------------------------------------------------------------- /packages/compiler-core/api-extractor.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../api-extractor.json", 3 | "mainEntryPointFilePath": "./dist/packages//src/index.d.ts", 4 | "dtsRollup": { 5 | "publicTrimmedFilePath": "./dist/.d.ts" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /packages/runtime-core/api-extractor.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../api-extractor.json", 3 | "mainEntryPointFilePath": "./dist/packages//src/index.d.ts", 4 | "dtsRollup": { 5 | "publicTrimmedFilePath": "./dist/.d.ts" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /packages/runtime-dom/api-extractor.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../api-extractor.json", 3 | "mainEntryPointFilePath": "./dist/packages//src/index.d.ts", 4 | "dtsRollup": { 5 | "publicTrimmedFilePath": "./dist/.d.ts" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /packages/runtime-test/api-extractor.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../api-extractor.json", 3 | "mainEntryPointFilePath": "./dist/packages//src/index.d.ts", 4 | "dtsRollup": { 5 | "publicTrimmedFilePath": "./dist/.d.ts" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /packages/server-renderer/api-extractor.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../api-extractor.json", 3 | "mainEntryPointFilePath": "./dist/packages//src/index.d.ts", 4 | "dtsRollup": { 5 | "publicTrimmedFilePath": "./dist/.d.ts" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /packages/size-check/brotli.js: -------------------------------------------------------------------------------- 1 | const { compress } = require('brotli') 2 | 3 | const file = require('fs').readFileSync('dist/index.js') 4 | const compressed = compress(file) 5 | const compressedSize = (compressed.length / 1024).toFixed(2) + 'kb' 6 | console.log(`brotli: ${compressedSize}`) 7 | -------------------------------------------------------------------------------- /packages/runtime-dom/README.md: -------------------------------------------------------------------------------- 1 | # @vue/runtime-dom 2 | 3 | ``` js 4 | import { h, createApp } from '@vue/runtime-dom' 5 | 6 | const RootComponent = { 7 | render() { 8 | return h('div', 'hello world') 9 | } 10 | } 11 | 12 | createApp(RootComponent).mount('#app') 13 | ``` 14 | -------------------------------------------------------------------------------- /packages/sfc-playground/public/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /packages/sfc-playground/src/main.ts: -------------------------------------------------------------------------------- 1 | import { createApp } from 'vue' 2 | import App from './App.vue' 3 | import '@vue/repl/style.css' 4 | 5 | // @ts-expect-error Custom window property 6 | window.VUE_DEVTOOLS_CONFIG = { 7 | defaultSelectedAppId: 'id:repl' 8 | } 9 | 10 | createApp(App).mount('#app') 11 | -------------------------------------------------------------------------------- /scripts/filter-e2e.js: -------------------------------------------------------------------------------- 1 | const e2eTests = ['/Transition', '/TransitionGroup', '/examples/'] 2 | 3 | module.exports = list => { 4 | return { 5 | filtered: list 6 | .filter(t => e2eTests.some(tt => t.includes(tt))) 7 | .map(test => ({ test })) 8 | } 9 | } 10 | 11 | module.exports.e2eTests = e2eTests 12 | -------------------------------------------------------------------------------- /packages/sfc-playground/src/download/template/README.md: -------------------------------------------------------------------------------- 1 | # Vite Vue Starter 2 | 3 | This is a project template using [Vite](https://vitejs.dev/). It requires [Node.js](https://nodejs.org) v12+. 4 | 5 | To start: 6 | 7 | ```sh 8 | npm install 9 | npm run dev 10 | 11 | # if using yarn: 12 | yarn 13 | yarn dev 14 | ``` 15 | -------------------------------------------------------------------------------- /packages/vue-compat/__tests__/utils.ts: -------------------------------------------------------------------------------- 1 | export function triggerEvent( 2 | target: Element, 3 | event: string, 4 | process?: (e: any) => any 5 | ) { 6 | const e = document.createEvent('HTMLEvents') 7 | e.initEvent(event, true, true) 8 | if (process) process(e) 9 | target.dispatchEvent(e) 10 | return e 11 | } 12 | -------------------------------------------------------------------------------- /packages/runtime-core/src/apiComputed.ts: -------------------------------------------------------------------------------- 1 | import { computed as _computed } from '@vue/reactivity' 2 | import { isInSSRComponentSetup } from './component' 3 | 4 | export const computed = ((getterOrOptions: any, debugOptions?: any) => { 5 | // @ts-ignore 6 | return _computed(getterOrOptions, debugOptions, isInSSRComponentSetup) 7 | }) as typeof _computed 8 | -------------------------------------------------------------------------------- /packages/server-renderer/src/helpers/ssrRenderSuspense.ts: -------------------------------------------------------------------------------- 1 | import { PushFn } from '../render' 2 | 3 | export async function ssrRenderSuspense( 4 | push: PushFn, 5 | { default: renderContent }: Record void) | undefined> 6 | ) { 7 | if (renderContent) { 8 | renderContent() 9 | } else { 10 | push(``) 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /packages/shared/src/typeUtils.ts: -------------------------------------------------------------------------------- 1 | export type UnionToIntersection = ( 2 | U extends any ? (k: U) => void : never 3 | ) extends (k: infer I) => void 4 | ? I 5 | : never 6 | 7 | // make keys required but keep undefined values 8 | export type LooseRequired = { [P in string & keyof T]: T[P] } 9 | 10 | export type IfAny = 0 extends (1 & T) ? Y : N 11 | -------------------------------------------------------------------------------- /test-dts/README.md: -------------------------------------------------------------------------------- 1 | # Test-ts 2 | 3 | Tests Typescript types to ensure the types remain as expected. 4 | 5 | ## Configuration 6 | 7 | ### tsconfig.json 8 | 9 | Config used to test against the package source 10 | 11 | ### tsconfig.build.json 12 | 13 | Replaces the `vue` and `@vue/*` dependencies with the built Typescript to ensure the published types are correct. 14 | -------------------------------------------------------------------------------- /packages/reactivity/src/operations.ts: -------------------------------------------------------------------------------- 1 | // using literal strings instead of numbers so that it's easier to inspect 2 | // debugger events 3 | 4 | export const enum TrackOpTypes { 5 | GET = 'get', 6 | HAS = 'has', 7 | ITERATE = 'iterate' 8 | } 9 | 10 | export const enum TriggerOpTypes { 11 | SET = 'set', 12 | ADD = 'add', 13 | DELETE = 'delete', 14 | CLEAR = 'clear' 15 | } 16 | -------------------------------------------------------------------------------- /packages/shared/src/globalsWhitelist.ts: -------------------------------------------------------------------------------- 1 | import { makeMap } from './makeMap' 2 | 3 | const GLOBALS_WHITE_LISTED = 4 | 'Infinity,undefined,NaN,isFinite,isNaN,parseFloat,parseInt,decodeURI,' + 5 | 'decodeURIComponent,encodeURI,encodeURIComponent,Math,Number,Date,Array,' + 6 | 'Object,Boolean,String,RegExp,Map,Set,JSON,Intl,BigInt' 7 | 8 | export const isGloballyWhitelisted = /*#__PURE__*/ makeMap(GLOBALS_WHITE_LISTED) 9 | -------------------------------------------------------------------------------- /packages/runtime-dom/__tests__/directives/vCloak.spec.ts: -------------------------------------------------------------------------------- 1 | import { createApp } from '@vue/runtime-dom' 2 | 3 | describe('vCloak', () => { 4 | test('should be removed after compile', () => { 5 | const root = document.createElement('div') 6 | root.setAttribute('v-cloak', '') 7 | createApp({ 8 | render() {} 9 | }).mount(root) 10 | expect(root.hasAttribute('v-cloak')).toBe(false) 11 | }) 12 | }) 13 | -------------------------------------------------------------------------------- /packages/template-explorer/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@vue/template-explorer", 3 | "version": "3.2.31", 4 | "private": true, 5 | "buildOptions": { 6 | "formats": [ 7 | "global" 8 | ], 9 | "compat": true, 10 | "env": "development", 11 | "enableNonBrowserBranches": true 12 | }, 13 | "dependencies": { 14 | "monaco-editor": "^0.20.0", 15 | "source-map": "^0.6.1" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /packages/vue/src/dev.ts: -------------------------------------------------------------------------------- 1 | import { initCustomFormatter } from '@vue/runtime-dom' 2 | 3 | export function initDev() { 4 | if (__BROWSER__) { 5 | if (!__ESM_BUNDLER__) { 6 | console.info( 7 | `You are running a development build of Vue.\n` + 8 | `Make sure to use the production build (*.prod.js) when deploying for production.` 9 | ) 10 | } 11 | 12 | initCustomFormatter() 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /packages/sfc-playground/src/download/template/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vite-vue-starter", 3 | "version": "0.0.0", 4 | "scripts": { 5 | "dev": "vite", 6 | "build": "vite build", 7 | "serve": "vite preview" 8 | }, 9 | "dependencies": { 10 | "vue": "^3.2.0" 11 | }, 12 | "devDependencies": { 13 | "@vitejs/plugin-vue": "^1.4.0", 14 | "@vue/compiler-sfc": "^3.2.0", 15 | "vite": "^2.4.4" 16 | } 17 | } -------------------------------------------------------------------------------- /packages/vue-compat/src/dev.ts: -------------------------------------------------------------------------------- 1 | import { initCustomFormatter } from '@vue/runtime-dom' 2 | 3 | export function initDev() { 4 | if (__BROWSER__) { 5 | if (!__ESM_BUNDLER__) { 6 | console.info( 7 | `You are running a development build of Vue.\n` + 8 | `Make sure to use the production build (*.prod.js) when deploying for production.` 9 | ) 10 | } 11 | 12 | initCustomFormatter() 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /packages/sfc-playground/src/download/template/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite App 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /packages/template-explorer/README.md: -------------------------------------------------------------------------------- 1 | ## Template Explorer 2 | 3 | Live explorer for template compilation output. 4 | 5 | To run: 6 | 7 | - `npm run dev-compiler` in repo root 8 | - When the compilation is done, in another terminal run `npm run open` 9 | 10 | Note: `index.html` uses CDN for dependencies and is continuously deployed at [https://template-explorer.vuejs.org](https://template-explorer.vuejs.org). For local development, use the scripts above. 11 | -------------------------------------------------------------------------------- /packages/compiler-dom/__tests__/index.spec.ts: -------------------------------------------------------------------------------- 1 | import { compile } from '../src' 2 | 3 | describe('compile', () => { 4 | it('should contain standard transforms', () => { 5 | const { code } = compile(`
6 |
7 |
test
8 |
red
9 |
`) 10 | 11 | expect(code).toMatchSnapshot() 12 | }) 13 | }) 14 | -------------------------------------------------------------------------------- /packages/shared/src/shapeFlags.ts: -------------------------------------------------------------------------------- 1 | export const enum ShapeFlags { 2 | ELEMENT = 1, 3 | FUNCTIONAL_COMPONENT = 1 << 1, 4 | STATEFUL_COMPONENT = 1 << 2, 5 | TEXT_CHILDREN = 1 << 3, 6 | ARRAY_CHILDREN = 1 << 4, 7 | SLOTS_CHILDREN = 1 << 5, 8 | TELEPORT = 1 << 6, 9 | SUSPENSE = 1 << 7, 10 | COMPONENT_SHOULD_KEEP_ALIVE = 1 << 8, 11 | COMPONENT_KEPT_ALIVE = 1 << 9, 12 | COMPONENT = ShapeFlags.STATEFUL_COMPONENT | ShapeFlags.FUNCTIONAL_COMPONENT 13 | } 14 | -------------------------------------------------------------------------------- /packages/runtime-core/types/refBail.d.ts: -------------------------------------------------------------------------------- 1 | // Note: this file is auto concatenated to the end of the bundled d.ts during 2 | // build. 3 | 4 | declare module '@vue/reactivity' { 5 | export interface RefUnwrapBailTypes { 6 | runtimeCoreBailTypes: 7 | | VNode 8 | | { 9 | // directly bailing on ComponentPublicInstance results in recursion 10 | // so we use this as a bail hint 11 | $: ComponentInternalInstance 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /packages/shared/__tests__/escapeHtml.spec.ts: -------------------------------------------------------------------------------- 1 | import { escapeHtml } from '../src' 2 | 3 | test('ssr: escapeHTML', () => { 4 | expect(escapeHtml(`foo`)).toBe(`foo`) 5 | expect(escapeHtml(true)).toBe(`true`) 6 | expect(escapeHtml(false)).toBe(`false`) 7 | expect(escapeHtml(`a && b`)).toBe(`a && b`) 8 | expect(escapeHtml(`"foo"`)).toBe(`"foo"`) 9 | expect(escapeHtml(`'bar'`)).toBe(`'bar'`) 10 | expect(escapeHtml(`
`)).toBe(`<div>`) 11 | }) 12 | -------------------------------------------------------------------------------- /packages/sfc-playground/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@vue/sfc-playground", 3 | "version": "3.2.31", 4 | "private": true, 5 | "scripts": { 6 | "dev": "vite", 7 | "build": "vite build", 8 | "serve": "vite preview" 9 | }, 10 | "devDependencies": { 11 | "@vitejs/plugin-vue": "^1.10.2", 12 | "vite": "^2.7.1" 13 | }, 14 | "dependencies": { 15 | "vue": "3.2.31", 16 | "@vue/repl": "^0.4.8", 17 | "file-saver": "^2.0.5", 18 | "jszip": "^3.6.0" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /packages/compiler-sfc/src/warn.ts: -------------------------------------------------------------------------------- 1 | const hasWarned: Record = {} 2 | 3 | export function warnOnce(msg: string) { 4 | const isNodeProd = 5 | typeof process !== 'undefined' && process.env.NODE_ENV === 'production' 6 | if (!isNodeProd && !__TEST__ && !hasWarned[msg]) { 7 | hasWarned[msg] = true 8 | warn(msg) 9 | } 10 | } 11 | 12 | export function warn(msg: string) { 13 | console.warn( 14 | `\x1b[1m\x1b[33m[@vue/compiler-sfc]\x1b[0m\x1b[33m ${msg}\x1b[0m\n` 15 | ) 16 | } 17 | -------------------------------------------------------------------------------- /packages/runtime-core/types/scriptSetupHelpers.d.ts: -------------------------------------------------------------------------------- 1 | // Note: this file is auto concatenated to the end of the bundled d.ts during 2 | // build. 3 | type _defineProps = typeof defineProps 4 | type _defineEmits = typeof defineEmits 5 | type _defineExpose = typeof defineExpose 6 | type _withDefaults = typeof withDefaults 7 | 8 | declare global { 9 | const defineProps: _defineProps 10 | const defineEmits: _defineEmits 11 | const defineExpose: _defineExpose 12 | const withDefaults: _withDefaults 13 | } 14 | -------------------------------------------------------------------------------- /packages/runtime-dom/__tests__/createApp.spec.ts: -------------------------------------------------------------------------------- 1 | import { createApp, h } from '../src' 2 | 3 | describe('createApp for dom', () => { 4 | // #2926 5 | test('mount to SVG container', () => { 6 | const root = document.createElementNS('http://www.w3.org/2000/svg', 'svg') 7 | createApp({ 8 | render() { 9 | return h('g') 10 | } 11 | }).mount(root) 12 | expect(root.children.length).toBe(1) 13 | expect(root.children[0] instanceof SVGElement).toBe(true) 14 | }) 15 | }) 16 | -------------------------------------------------------------------------------- /packages/vue/macros-global.d.ts: -------------------------------------------------------------------------------- 1 | import { 2 | $ as _$, 3 | $$ as _$$, 4 | $ref as _$ref, 5 | $shallowRef as _$shallowRef, 6 | $computed as _$computed, 7 | $customRef as _$customRef, 8 | $toRef as _$toRef 9 | } from './macros' 10 | 11 | declare global { 12 | const $: typeof _$ 13 | const $$: typeof _$$ 14 | const $ref: typeof _$ref 15 | const $shallowRef: typeof _$shallowRef 16 | const $computed: typeof _$computed 17 | const $customRef: typeof _$customRef 18 | const $toRef: typeof _$toRef 19 | } 20 | -------------------------------------------------------------------------------- /BACKERS.md: -------------------------------------------------------------------------------- 1 |

Sponsors & Backers

2 | 3 | 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 [ sponsor Vue's development](https://vuejs.org/sponsor/). 4 | 5 |

6 | 7 | sponsors 8 | 9 |

10 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: Create new issue 4 | url: https://new-issue.vuejs.org/?repo=vuejs/core 5 | about: Please use the following link to create a new issue. 6 | - name: Patreon 7 | url: https://www.patreon.com/evanyou 8 | about: Love Vue.js? Please consider supporting us via Patreon. 9 | - name: Open Collective 10 | url: https://opencollective.com/vuejs/donate 11 | about: Love Vue.js? Please consider supporting us via Open Collective. 12 | -------------------------------------------------------------------------------- /packages/sfc-playground/src/icons/Share.vue: -------------------------------------------------------------------------------- 1 | 18 | -------------------------------------------------------------------------------- /packages/compiler-sfc/src/stylePluginTrim.ts: -------------------------------------------------------------------------------- 1 | import { PluginCreator } from 'postcss' 2 | 3 | const trimPlugin: PluginCreator<{}> = () => { 4 | return { 5 | postcssPlugin: 'vue-sfc-trim', 6 | Once(root) { 7 | root.walk(({ type, raws }) => { 8 | if (type === 'rule' || type === 'atrule') { 9 | if (raws.before) raws.before = '\n' 10 | if ('after' in raws && raws.after) raws.after = '\n' 11 | } 12 | }) 13 | } 14 | } 15 | } 16 | 17 | trimPlugin.postcss = true 18 | export default trimPlugin 19 | -------------------------------------------------------------------------------- /packages/compiler-dom/src/decodeHtmlBrowser.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-restricted-globals */ 2 | 3 | let decoder: HTMLDivElement 4 | 5 | export function decodeHtmlBrowser(raw: string, asAttr = false): string { 6 | if (!decoder) { 7 | decoder = document.createElement('div') 8 | } 9 | if (asAttr) { 10 | decoder.innerHTML = `
` 11 | return decoder.children[0].getAttribute('foo') as string 12 | } else { 13 | decoder.innerHTML = raw 14 | return decoder.textContent as string 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /test-dts/inject.test-d.ts: -------------------------------------------------------------------------------- 1 | import { provide, inject, InjectionKey, expectType } from './index' 2 | 3 | const key: InjectionKey = Symbol() 4 | 5 | provide(key, 1) 6 | // @ts-expect-error 7 | provide(key, 'foo') 8 | 9 | expectType(inject(key)) 10 | expectType(inject(key, 1)) 11 | expectType(inject(key, () => 1, true /* treatDefaultAsFactory */)) 12 | 13 | expectType<() => number>(inject('foo', () => 1)) 14 | expectType<() => number>(inject('foo', () => 1, false)) 15 | expectType(inject('foo', () => 1, true)) 16 | -------------------------------------------------------------------------------- /packages/sfc-playground/src/icons/Moon.vue: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /packages/compiler-dom/src/transforms/vShow.ts: -------------------------------------------------------------------------------- 1 | import { DirectiveTransform } from '@vue/compiler-core' 2 | import { createDOMCompilerError, DOMErrorCodes } from '../errors' 3 | import { V_SHOW } from '../runtimeHelpers' 4 | 5 | export const transformShow: DirectiveTransform = (dir, node, context) => { 6 | const { exp, loc } = dir 7 | if (!exp) { 8 | context.onError( 9 | createDOMCompilerError(DOMErrorCodes.X_V_SHOW_NO_EXPRESSION, loc) 10 | ) 11 | } 12 | 13 | return { 14 | props: [], 15 | needRuntime: context.helper(V_SHOW) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /packages/runtime-core/src/helpers/toHandlers.ts: -------------------------------------------------------------------------------- 1 | import { toHandlerKey, isObject } from '@vue/shared' 2 | import { warn } from '../warning' 3 | 4 | /** 5 | * For prefixing keys in v-on="obj" with "on" 6 | * @private 7 | */ 8 | export function toHandlers(obj: Record): Record { 9 | const ret: Record = {} 10 | if (__DEV__ && !isObject(obj)) { 11 | warn(`v-on with no argument expects an object value.`) 12 | return ret 13 | } 14 | for (const key in obj) { 15 | ret[toHandlerKey(key)] = obj[key] 16 | } 17 | return ret 18 | } 19 | -------------------------------------------------------------------------------- /packages/compiler-dom/__tests__/transforms/__snapshots__/vShow.spec.ts.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`compiler: v-show transform simple expression 1`] = ` 4 | "const _Vue = Vue 5 | 6 | return function render(_ctx, _cache) { 7 | with (_ctx) { 8 | const { vShow: _vShow, withDirectives: _withDirectives, openBlock: _openBlock, createElementBlock: _createElementBlock } = _Vue 9 | 10 | return _withDirectives((_openBlock(), _createElementBlock(\\"div\\", null, null, 512 /* NEED_PATCH */)), [ 11 | [_vShow, a] 12 | ]) 13 | } 14 | }" 15 | `; 16 | -------------------------------------------------------------------------------- /packages/shared/__tests__/normalizeProp.spec.ts: -------------------------------------------------------------------------------- 1 | import { normalizeClass } from '../src' 2 | 3 | describe('normalizeClass', () => { 4 | test('handles string correctly', () => { 5 | expect(normalizeClass('foo')).toEqual('foo') 6 | }) 7 | 8 | test('handles array correctly', () => { 9 | expect(normalizeClass(['foo', undefined, true, false, 'bar'])).toEqual( 10 | 'foo bar' 11 | ) 12 | }) 13 | 14 | test('handles object correctly', () => { 15 | expect(normalizeClass({ foo: true, bar: false, baz: true })).toEqual( 16 | 'foo baz' 17 | ) 18 | }) 19 | }) 20 | -------------------------------------------------------------------------------- /packages/runtime-core/__tests__/misc.spec.ts: -------------------------------------------------------------------------------- 1 | import { render, h, nodeOps, reactive, isReactive } from '@vue/runtime-test' 2 | 3 | describe('misc', () => { 4 | test('component public instance should not be observable', () => { 5 | let instance: any 6 | const Comp = { 7 | render() {}, 8 | mounted() { 9 | instance = this 10 | } 11 | } 12 | render(h(Comp), nodeOps.createElement('div')) 13 | expect(instance).toBeDefined() 14 | const r = reactive(instance) 15 | expect(r).toBe(instance) 16 | expect(isReactive(r)).toBe(false) 17 | }) 18 | }) 19 | -------------------------------------------------------------------------------- /packages/runtime-core/src/compat/data.ts: -------------------------------------------------------------------------------- 1 | import { isPlainObject } from '@vue/shared' 2 | import { DeprecationTypes, warnDeprecation } from './compatConfig' 3 | 4 | export function deepMergeData(to: any, from: any) { 5 | for (const key in from) { 6 | const toVal = to[key] 7 | const fromVal = from[key] 8 | if (key in to && isPlainObject(toVal) && isPlainObject(fromVal)) { 9 | __DEV__ && warnDeprecation(DeprecationTypes.OPTIONS_DATA_MERGE, null, key) 10 | deepMergeData(toVal, fromVal) 11 | } else { 12 | to[key] = fromVal 13 | } 14 | } 15 | return to 16 | } 17 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use the project's typescript version 3 | "typescript.tsdk": "node_modules/typescript/lib", 4 | 5 | "cSpell.enabledLanguageIds": ["markdown", "plaintext", "text", "yml"], 6 | 7 | // Use prettier to format typescript, javascript and JSON files 8 | "[typescript]": { 9 | "editor.defaultFormatter": "esbenp.prettier-vscode" 10 | }, 11 | "[javascript]": { 12 | "editor.defaultFormatter": "esbenp.prettier-vscode" 13 | }, 14 | "[json]": { 15 | "editor.defaultFormatter": "esbenp.prettier-vscode" 16 | }, 17 | "commentTranslate.source": "Baidu" 18 | } 19 | -------------------------------------------------------------------------------- /packages/runtime-test/src/triggerEvent.ts: -------------------------------------------------------------------------------- 1 | import { isArray } from '@vue/shared' 2 | import { TestElement } from './nodeOps' 3 | 4 | export function triggerEvent( 5 | el: TestElement, 6 | event: string, 7 | payload: any[] = [] 8 | ) { 9 | const { eventListeners } = el 10 | if (eventListeners) { 11 | const listener = eventListeners[event] 12 | if (listener) { 13 | if (isArray(listener)) { 14 | for (let i = 0; i < listener.length; i++) { 15 | listener[i](...payload) 16 | } 17 | } else { 18 | listener(...payload) 19 | } 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /packages/compiler-dom/src/transforms/ignoreSideEffectTags.ts: -------------------------------------------------------------------------------- 1 | import { NodeTransform, NodeTypes, ElementTypes } from '@vue/compiler-core' 2 | import { DOMErrorCodes, createDOMCompilerError } from '../errors' 3 | 4 | export const ignoreSideEffectTags: NodeTransform = (node, context) => { 5 | if ( 6 | node.type === NodeTypes.ELEMENT && 7 | node.tagType === ElementTypes.ELEMENT && 8 | (node.tag === 'script' || node.tag === 'style') 9 | ) { 10 | context.onError( 11 | createDOMCompilerError(DOMErrorCodes.X_IGNORED_SIDE_EFFECT_TAG, node.loc) 12 | ) 13 | context.removeNode() 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /packages/runtime-test/src/patchProp.ts: -------------------------------------------------------------------------------- 1 | import { TestElement, logNodeOp, NodeOpTypes } from './nodeOps' 2 | import { isOn } from '@vue/shared' 3 | 4 | export function patchProp( 5 | el: TestElement, 6 | key: string, 7 | prevValue: any, 8 | nextValue: any 9 | ) { 10 | logNodeOp({ 11 | type: NodeOpTypes.PATCH, 12 | targetNode: el, 13 | propKey: key, 14 | propPrevValue: prevValue, 15 | propNextValue: nextValue 16 | }) 17 | el.props[key] = nextValue 18 | if (isOn(key)) { 19 | const event = key.slice(2).toLowerCase() 20 | ;(el.eventListeners || (el.eventListeners = {}))[event] = nextValue 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /test-dts/index.d.ts: -------------------------------------------------------------------------------- 1 | // This directory contains a number of d.ts assertions 2 | // use \@ts-expect-error where errors are expected. 3 | 4 | export * from '@vue/runtime-dom' 5 | 6 | export function describe(_name: string, _fn: () => void): void 7 | 8 | export function expectType(value: T): void 9 | export function expectError(value: T): void 10 | export function expectAssignable(value: T2): void 11 | 12 | export type IsUnion = (T extends any 13 | ? (U extends T ? false : true) 14 | : never) extends false 15 | ? false 16 | : true 17 | 18 | export type IsAny = 0 extends (1 & T) ? true : false 19 | -------------------------------------------------------------------------------- /packages/compiler-ssr/__tests__/utils.ts: -------------------------------------------------------------------------------- 1 | import { compile } from '../src' 2 | 3 | export function getCompiledString(src: string): string { 4 | // Wrap src template in a root div so that it doesn't get injected 5 | // fallthrough attr. This results in less noise in generated snapshots 6 | // but also means this util can only be used for non-root cases. 7 | const { code } = compile(`
${src}
`) 8 | const match = code.match( 9 | /_push\(\`([^]*)<\/div>\`\)/ 10 | ) 11 | 12 | if (!match) { 13 | throw new Error(`Unexpected compile result:\n${code}`) 14 | } 15 | 16 | return `\`${match[1]}\`` 17 | } 18 | -------------------------------------------------------------------------------- /packages/runtime-core/__tests__/helpers/toHandlers.spec.ts: -------------------------------------------------------------------------------- 1 | import { toHandlers } from '../../src/helpers/toHandlers' 2 | 3 | describe('toHandlers', () => { 4 | it('should not accept non-objects', () => { 5 | toHandlers(null as any) 6 | toHandlers(undefined as any) 7 | 8 | expect( 9 | 'v-on with no argument expects an object value.' 10 | ).toHaveBeenWarnedTimes(2) 11 | }) 12 | 13 | it('should properly change object keys', () => { 14 | const input = () => {} 15 | const change = () => {} 16 | 17 | expect(toHandlers({ input, change })).toStrictEqual({ 18 | onInput: input, 19 | onChange: change 20 | }) 21 | }) 22 | }) 23 | -------------------------------------------------------------------------------- /packages/server-renderer/src/helpers/ssrRenderComponent.ts: -------------------------------------------------------------------------------- 1 | import { Component, ComponentInternalInstance, createVNode, Slots } from 'vue' 2 | import { Props, renderComponentVNode, SSRBuffer } from '../render' 3 | import { SSRSlots } from './ssrRenderSlot' 4 | 5 | export function ssrRenderComponent( 6 | comp: Component, 7 | props: Props | null = null, 8 | children: Slots | SSRSlots | null = null, 9 | parentComponent: ComponentInternalInstance | null = null, 10 | slotScopeId?: string 11 | ): SSRBuffer | Promise { 12 | return renderComponentVNode( 13 | createVNode(comp, props, children), 14 | parentComponent, 15 | slotScopeId 16 | ) 17 | } 18 | -------------------------------------------------------------------------------- /packages/runtime-core/src/helpers/useSsrContext.ts: -------------------------------------------------------------------------------- 1 | import { inject } from '../apiInject' 2 | import { warn } from '../warning' 3 | 4 | export const ssrContextKey = Symbol(__DEV__ ? `ssrContext` : ``) 5 | 6 | export const useSSRContext = >() => { 7 | if (!__GLOBAL__) { 8 | const ctx = inject(ssrContextKey) 9 | if (!ctx) { 10 | warn( 11 | `Server rendering context not provided. Make sure to only call ` + 12 | `useSSRContext() conditionally in the server build.` 13 | ) 14 | } 15 | return ctx 16 | } else if (__DEV__) { 17 | warn(`useSSRContext() is not supported in the global build.`) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /packages/shared/src/makeMap.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Make a map and return a function for checking if a key 3 | * is in that map. 4 | * IMPORTANT: all calls of this function must be prefixed with 5 | * \/\*#\_\_PURE\_\_\*\/ 6 | * So that rollup can tree-shake them if necessary. 7 | */ 8 | export function makeMap( 9 | str: string, 10 | expectsLowerCase?: boolean 11 | ): (key: string) => boolean { 12 | const map: Record = Object.create(null) 13 | const list: Array = str.split(',') 14 | for (let i = 0; i < list.length; i++) { 15 | map[list[i]] = true 16 | } 17 | return expectsLowerCase ? val => !!map[val.toLowerCase()] : val => !!map[val] 18 | } 19 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Reporting a Vulnerability 2 | 3 | To report a vulnerability, please email security@vuejs.org. 4 | 5 | While the discovery of new vulnerabilities is rare, we also recommend always using the latest versions of Vue and its official companion libraries to ensure your application remains as secure as possible. 6 | 7 | Please note that we do not consider XSS via template expressions a valid attack vector, because it can only happen if the user intentionally uses untrusted content as template compilation source. This is similar to knowingly pasting untrusted scripts into a browser console. We explicitly warn users against using untrusted content as template compilation source in our documentation. 8 | -------------------------------------------------------------------------------- /packages/server-renderer/src/helpers/ssrGetDirectiveProps.ts: -------------------------------------------------------------------------------- 1 | import { ComponentPublicInstance, Directive } from '@vue/runtime-core' 2 | 3 | export function ssrGetDirectiveProps( 4 | instance: ComponentPublicInstance, 5 | dir: Directive, 6 | value?: any, 7 | arg?: string, 8 | modifiers: Record = {} 9 | ): Record { 10 | if (typeof dir !== 'function' && dir.getSSRProps) { 11 | return ( 12 | dir.getSSRProps( 13 | { 14 | dir, 15 | instance, 16 | value, 17 | oldValue: undefined, 18 | arg, 19 | modifiers 20 | }, 21 | null as any 22 | ) || {} 23 | ) 24 | } 25 | return {} 26 | } 27 | -------------------------------------------------------------------------------- /.github/workflows/release-tag.yml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | tags: 4 | - 'v*' # Push events to matching v*, i.e. v1.0, v20.15.10 5 | 6 | name: Create Release 7 | 8 | jobs: 9 | build: 10 | name: Create Release 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: Checkout code 14 | uses: actions/checkout@master 15 | - name: Create Release for Tag 16 | id: release_tag 17 | uses: yyx990803/release-tag@master 18 | env: 19 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 20 | with: 21 | tag_name: ${{ github.ref }} 22 | body: | 23 | Please refer to [CHANGELOG.md](https://github.com/vuejs/core/blob/main/CHANGELOG.md) for details. 24 | -------------------------------------------------------------------------------- /packages/runtime-core/src/compat/instanceListeners.ts: -------------------------------------------------------------------------------- 1 | import { isOn } from '@vue/shared' 2 | import { ComponentInternalInstance } from '../component' 3 | import { assertCompatEnabled, DeprecationTypes } from './compatConfig' 4 | 5 | export function getCompatListeners(instance: ComponentInternalInstance) { 6 | assertCompatEnabled(DeprecationTypes.INSTANCE_LISTENERS, instance) 7 | 8 | const listeners: Record = {} 9 | const rawProps = instance.vnode.props 10 | if (!rawProps) { 11 | return listeners 12 | } 13 | for (const key in rawProps) { 14 | if (isOn(key)) { 15 | listeners[key[2].toLowerCase() + key.slice(3)] = rawProps[key] 16 | } 17 | } 18 | return listeners 19 | } 20 | -------------------------------------------------------------------------------- /packages/runtime-test/README.md: -------------------------------------------------------------------------------- 1 | # @vue/runtime-test 2 | 3 | This is for Vue's own internal tests only - it ensures logic tested using this package is DOM-agnostic, and it runs faster than JSDOM. 4 | 5 | It can also be used as a reference for implementing a custom renderer. 6 | 7 | ``` js 8 | import { h, render, nodeOps, dumpOps } from '@vue/runtime-test' 9 | 10 | const App = { 11 | data () { 12 | return { 13 | msg: 'Hello World!' 14 | } 15 | } 16 | render () { 17 | return h('div', this.msg) 18 | } 19 | } 20 | 21 | // root is of type `TestElement` as defined in src/nodeOps.ts 22 | const root = nodeOps.createElement('div') 23 | render(h(App), root) 24 | 25 | const ops = dumpOps() 26 | console.log(ops) 27 | ``` 28 | -------------------------------------------------------------------------------- /packages/template-explorer/local.html: -------------------------------------------------------------------------------- 1 | Vue Template Explorer 2 | 3 | 4 | 5 | 6 |
7 |
8 | 9 | 10 | 17 | 18 | 21 | -------------------------------------------------------------------------------- /test-dts/reactivity.test-d.ts: -------------------------------------------------------------------------------- 1 | import { shallowReadonly } from '@vue/reactivity' 2 | import { ref, readonly, describe, expectError, expectType, Ref } from './index' 3 | 4 | describe('should support DeepReadonly', () => { 5 | const r = readonly({ obj: { k: 'v' } }) 6 | // @ts-expect-error 7 | expectError((r.obj = {})) 8 | // @ts-expect-error 9 | expectError((r.obj.k = 'x')) 10 | }) 11 | 12 | // #4180 13 | describe('readonly ref', () => { 14 | const r = readonly(ref({ count: 1 })) 15 | expectType(r) 16 | }) 17 | 18 | describe('shallowReadonly ref unwrap', () => { 19 | const r = shallowReadonly({ count: { n: ref(1) } }) 20 | // @ts-expect-error 21 | r.count = 2 22 | expectType(r.count.n) 23 | r.count.n.value = 123 24 | }) 25 | -------------------------------------------------------------------------------- /packages/compiler-sfc/__tests__/__snapshots__/compileTemplate.spec.ts.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`source map 1`] = ` 4 | Object { 5 | "mappings": ";;;wBACE,oBAA8B;IAAzB,oBAAmB,4BAAbA,WAAM", 6 | "names": Array [ 7 | "render", 8 | ], 9 | "sources": Array [ 10 | "example.vue", 11 | ], 12 | "sourcesContent": Array [ 13 | " 14 |

{{ render }}

15 | ", 16 | ], 17 | "version": 3, 18 | } 19 | `; 20 | 21 | exports[`template errors 1`] = ` 22 | Array [ 23 | [SyntaxError: Error parsing JavaScript expression: Unexpected token (1:3)], 24 | [SyntaxError: v-bind is missing expression.], 25 | [SyntaxError: v-model can only be used on , 7 |
8 |
9 | 10 | 27 | 28 | 63 | -------------------------------------------------------------------------------- /packages/compiler-ssr/src/transforms/ssrTransformTransitionGroup.ts: -------------------------------------------------------------------------------- 1 | import { ComponentNode, findProp, NodeTypes } from '@vue/compiler-dom' 2 | import { processChildren, SSRTransformContext } from '../ssrCodegenTransform' 3 | 4 | export function ssrProcessTransitionGroup( 5 | node: ComponentNode, 6 | context: SSRTransformContext 7 | ) { 8 | const tag = findProp(node, 'tag') 9 | if (tag) { 10 | if (tag.type === NodeTypes.DIRECTIVE) { 11 | // dynamic :tag 12 | context.pushStringPart(`<`) 13 | context.pushStringPart(tag.exp!) 14 | context.pushStringPart(`>`) 15 | 16 | processChildren( 17 | node.children, 18 | context, 19 | false, 20 | /** 21 | * TransitionGroup has the special runtime behavior of flattening and 22 | * concatenating all children into a single fragment (in order for them to 23 | * be patched using the same key map) so we need to account for that here 24 | * by disabling nested fragment wrappers from being generated. 25 | */ 26 | true 27 | ) 28 | context.pushStringPart(``) 31 | } else { 32 | // static tag 33 | context.pushStringPart(`<${tag.value!.content}>`) 34 | processChildren(node.children, context, false, true) 35 | context.pushStringPart(``) 36 | } 37 | } else { 38 | // fragment 39 | processChildren(node.children, context, true, true) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /packages/vue-compat/src/createCompatVue.ts: -------------------------------------------------------------------------------- 1 | // This entry exports the runtime only, and is built as 2 | // `dist/vue.esm-bundler.js` which is used by default for bundlers. 3 | import { initDev } from './dev' 4 | import { 5 | compatUtils, 6 | createApp, 7 | Transition, 8 | TransitionGroup, 9 | KeepAlive, 10 | DeprecationTypes, 11 | vShow, 12 | vModelDynamic 13 | } from '@vue/runtime-dom' 14 | import { extend } from '@vue/shared' 15 | 16 | if (__DEV__) { 17 | initDev() 18 | } 19 | 20 | import * as runtimeDom from '@vue/runtime-dom' 21 | 22 | function wrappedCreateApp(...args: any[]) { 23 | // @ts-ignore 24 | const app = createApp(...args) 25 | if (compatUtils.isCompatEnabled(DeprecationTypes.RENDER_FUNCTION, null)) { 26 | // register built-in components so that they can be resolved via strings 27 | // in the legacy h() call. The __compat__ prefix is to ensure that v3 h() 28 | // doesn't get affected. 29 | app.component('__compat__transition', Transition) 30 | app.component('__compat__transition-group', TransitionGroup) 31 | app.component('__compat__keep-alive', KeepAlive) 32 | // built-in directives. No need for prefix since there's no render fn API 33 | // for resolving directives via string in v3. 34 | app._context.directives.show = vShow 35 | app._context.directives.model = vModelDynamic 36 | } 37 | return app 38 | } 39 | 40 | export function createCompatVue() { 41 | const Vue = compatUtils.createCompatVue(createApp, wrappedCreateApp) 42 | extend(Vue, runtimeDom) 43 | return Vue 44 | } 45 | -------------------------------------------------------------------------------- /packages/vue/examples/__tests__/markdown.spec.ts: -------------------------------------------------------------------------------- 1 | import path from 'path' 2 | import { 3 | setupPuppeteer, 4 | expectByPolling, 5 | E2E_TIMEOUT 6 | } from '../../__tests__/e2eUtils' 7 | 8 | describe('e2e: markdown', () => { 9 | const { page, isVisible, value, html } = setupPuppeteer() 10 | 11 | async function testMarkdown(apiType: 'classic' | 'composition') { 12 | const baseUrl = `file://${path.resolve( 13 | __dirname, 14 | `../${apiType}/markdown.html#test` 15 | )}` 16 | 17 | await page().goto(baseUrl) 18 | expect(await isVisible('#editor')).toBe(true) 19 | expect(await value('textarea')).toBe('# hello') 20 | expect(await html('#editor div')).toBe('

hello

\n') 21 | 22 | await page().type('textarea', '\n## foo\n\n- bar\n- baz') 23 | 24 | // assert the output is not updated yet because of debounce 25 | // debounce has become unstable on CI so this assertion is disabled 26 | // expect(await html('#editor div')).toBe('

hello

\n') 27 | 28 | await expectByPolling( 29 | () => html('#editor div'), 30 | '

hello

\n' + 31 | '

foo

\n' + 32 | '
    \n
  • bar
  • \n
  • baz
  • \n
\n' 33 | ) 34 | } 35 | 36 | test( 37 | 'classic', 38 | async () => { 39 | await testMarkdown('classic') 40 | }, 41 | E2E_TIMEOUT 42 | ) 43 | 44 | test( 45 | 'composition', 46 | async () => { 47 | await testMarkdown('composition') 48 | }, 49 | E2E_TIMEOUT 50 | ) 51 | }) 52 | -------------------------------------------------------------------------------- /packages/vue/examples/composition/markdown.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |
6 | 7 |
8 |
9 | 10 | 27 | 28 | 63 | -------------------------------------------------------------------------------- /packages/compiler-dom/src/transforms/transformStyle.ts: -------------------------------------------------------------------------------- 1 | import { 2 | NodeTransform, 3 | NodeTypes, 4 | createSimpleExpression, 5 | SimpleExpressionNode, 6 | SourceLocation, 7 | ConstantTypes 8 | } from '@vue/compiler-core' 9 | import { parseStringStyle } from '@vue/shared' 10 | 11 | // Parse inline CSS strings for static style attributes into an object. 12 | // This is a NodeTransform since it works on the static `style` attribute and 13 | // converts it into a dynamic equivalent: 14 | // style="color: red" -> :style='{ "color": "red" }' 15 | // It is then processed by `transformElement` and included in the generated 16 | // props. 17 | export const transformStyle: NodeTransform = node => { 18 | if (node.type === NodeTypes.ELEMENT) { 19 | node.props.forEach((p, i) => { 20 | if (p.type === NodeTypes.ATTRIBUTE && p.name === 'style' && p.value) { 21 | // replace p with an expression node 22 | node.props[i] = { 23 | type: NodeTypes.DIRECTIVE, 24 | name: `bind`, 25 | arg: createSimpleExpression(`style`, true, p.loc), 26 | exp: parseInlineCSS(p.value.content, p.loc), 27 | modifiers: [], 28 | loc: p.loc 29 | } 30 | } 31 | }) 32 | } 33 | } 34 | 35 | const parseInlineCSS = ( 36 | cssText: string, 37 | loc: SourceLocation 38 | ): SimpleExpressionNode => { 39 | const normalized = parseStringStyle(cssText) 40 | return createSimpleExpression( 41 | JSON.stringify(normalized), 42 | false, 43 | loc, 44 | ConstantTypes.CAN_STRINGIFY 45 | ) 46 | } 47 | -------------------------------------------------------------------------------- /packages/compiler-core/__tests__/transforms/vMemo.spec.ts: -------------------------------------------------------------------------------- 1 | import { baseCompile } from '../../src' 2 | 3 | describe('compiler: v-memo transform', () => { 4 | function compile(content: string) { 5 | return baseCompile(`
${content}
`, { 6 | mode: 'module', 7 | prefixIdentifiers: true 8 | }).code 9 | } 10 | 11 | test('on root element', () => { 12 | expect( 13 | baseCompile(`
`, { 14 | mode: 'module', 15 | prefixIdentifiers: true 16 | }).code 17 | ).toMatchSnapshot() 18 | }) 19 | 20 | test('on normal element', () => { 21 | expect(compile(`
`)).toMatchSnapshot() 22 | }) 23 | 24 | test('on component', () => { 25 | expect(compile(``)).toMatchSnapshot() 26 | }) 27 | 28 | test('on v-if', () => { 29 | expect( 30 | compile( 31 | `
foobar
32 | ` 33 | ) 34 | ).toMatchSnapshot() 35 | }) 36 | 37 | test('on v-for', () => { 38 | expect( 39 | compile( 40 | `
41 | foobar 42 |
` 43 | ) 44 | ).toMatchSnapshot() 45 | }) 46 | 47 | test('on template v-for', () => { 48 | expect( 49 | compile( 50 | `` 53 | ) 54 | ).toMatchSnapshot() 55 | }) 56 | }) 57 | -------------------------------------------------------------------------------- /packages/compiler-ssr/__tests__/ssrPortal.spec.ts: -------------------------------------------------------------------------------- 1 | import { compile } from '../src' 2 | 3 | describe('ssr compile: teleport', () => { 4 | test('should work', () => { 5 | expect(compile(`
`).code) 6 | .toMatchInlineSnapshot(` 7 | "const { ssrRenderTeleport: _ssrRenderTeleport } = require(\\"vue/server-renderer\\") 8 | 9 | return function ssrRender(_ctx, _push, _parent, _attrs) { 10 | _ssrRenderTeleport(_push, (_push) => { 11 | _push(\`
\`) 12 | }, _ctx.target, false, _parent) 13 | }" 14 | `) 15 | }) 16 | 17 | test('disabled prop handling', () => { 18 | expect(compile(`
`).code) 19 | .toMatchInlineSnapshot(` 20 | "const { ssrRenderTeleport: _ssrRenderTeleport } = require(\\"vue/server-renderer\\") 21 | 22 | return function ssrRender(_ctx, _push, _parent, _attrs) { 23 | _ssrRenderTeleport(_push, (_push) => { 24 | _push(\`
\`) 25 | }, _ctx.target, true, _parent) 26 | }" 27 | `) 28 | 29 | expect( 30 | compile(`
`).code 31 | ).toMatchInlineSnapshot(` 32 | "const { ssrRenderTeleport: _ssrRenderTeleport } = require(\\"vue/server-renderer\\") 33 | 34 | return function ssrRender(_ctx, _push, _parent, _attrs) { 35 | _ssrRenderTeleport(_push, (_push) => { 36 | _push(\`
\`) 37 | }, _ctx.target, _ctx.foo, _parent) 38 | }" 39 | `) 40 | }) 41 | }) 42 | -------------------------------------------------------------------------------- /packages/reactivity/src/dep.ts: -------------------------------------------------------------------------------- 1 | import { ReactiveEffect, trackOpBit } from './effect' 2 | 3 | export type Dep = Set & TrackedMarkers 4 | 5 | /** 6 | * wasTracked and newTracked maintain the status for several levels of effect 7 | * tracking recursion. One bit per level is used to define whether the dependency 8 | * was/is tracked. 9 | */ 10 | type TrackedMarkers = { 11 | /** 12 | * wasTracked 13 | */ 14 | w: number 15 | /** 16 | * newTracked 17 | */ 18 | n: number 19 | } 20 | 21 | // 创建一个 set 集合 22 | export const createDep = (effects?: ReactiveEffect[]): Dep => { 23 | const dep = new Set(effects) as Dep 24 | dep.w = 0 25 | dep.n = 0 26 | return dep 27 | } 28 | 29 | export const wasTracked = (dep: Dep): boolean => (dep.w & trackOpBit) > 0 30 | 31 | export const newTracked = (dep: Dep): boolean => (dep.n & trackOpBit) > 0 32 | 33 | export const initDepMarkers = ({ deps }: ReactiveEffect) => { 34 | if (deps.length) { 35 | for (let i = 0; i < deps.length; i++) { 36 | deps[i].w |= trackOpBit // set was tracked 37 | } 38 | } 39 | } 40 | 41 | export const finalizeDepMarkers = (effect: ReactiveEffect) => { 42 | const { deps } = effect 43 | if (deps.length) { 44 | let ptr = 0 45 | for (let i = 0; i < deps.length; i++) { 46 | const dep = deps[i] 47 | if (wasTracked(dep) && !newTracked(dep)) { 48 | dep.delete(effect) 49 | } else { 50 | deps[ptr++] = dep 51 | } 52 | // clear bits 53 | dep.w &= ~trackOpBit 54 | dep.n &= ~trackOpBit 55 | } 56 | deps.length = ptr 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /packages/runtime-dom/src/directives/vShow.ts: -------------------------------------------------------------------------------- 1 | import { ObjectDirective } from '@vue/runtime-core' 2 | 3 | interface VShowElement extends HTMLElement { 4 | // _vod = vue original display 5 | _vod: string 6 | } 7 | 8 | export const vShow: ObjectDirective = { 9 | beforeMount(el, { value }, { transition }) { 10 | el._vod = el.style.display === 'none' ? '' : el.style.display 11 | if (transition && value) { 12 | transition.beforeEnter(el) 13 | } else { 14 | setDisplay(el, value) 15 | } 16 | }, 17 | mounted(el, { value }, { transition }) { 18 | if (transition && value) { 19 | transition.enter(el) 20 | } 21 | }, 22 | updated(el, { value, oldValue }, { transition }) { 23 | if (!value === !oldValue) return 24 | if (transition) { 25 | if (value) { 26 | transition.beforeEnter(el) 27 | setDisplay(el, true) 28 | transition.enter(el) 29 | } else { 30 | transition.leave(el, () => { 31 | setDisplay(el, false) 32 | }) 33 | } 34 | } else { 35 | setDisplay(el, value) 36 | } 37 | }, 38 | beforeUnmount(el, { value }) { 39 | setDisplay(el, value) 40 | } 41 | } 42 | 43 | function setDisplay(el: VShowElement, value: unknown): void { 44 | el.style.display = value ? el._vod : 'none' 45 | } 46 | 47 | // SSR vnode transforms, only used when user includes client-oriented render 48 | // function in SSR 49 | export function initVShowForSSR() { 50 | vShow.getSSRProps = ({ value }) => { 51 | if (!value) { 52 | return { style: { display: 'none' } } 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /packages/compiler-dom/__tests__/decoderHtmlBrowser.spec.ts: -------------------------------------------------------------------------------- 1 | import { decodeHtmlBrowser } from '../src/decodeHtmlBrowser' 2 | 3 | describe('decodeHtmlBrowser', () => { 4 | it('should decode HTML correctly', () => { 5 | expect(decodeHtmlBrowser(' abc 123 ')).toBe(' abc 123 ') 6 | 7 | expect(decodeHtmlBrowser('&')).toBe('&') 8 | expect(decodeHtmlBrowser('&')).toBe('&') 9 | expect(decodeHtmlBrowser('&amp;')).toBe('&') 10 | 11 | expect(decodeHtmlBrowser('<')).toBe('<') 12 | expect(decodeHtmlBrowser('<')).toBe('<') 13 | expect(decodeHtmlBrowser('&lt;')).toBe('<') 14 | 15 | expect(decodeHtmlBrowser('>')).toBe('>') 16 | expect(decodeHtmlBrowser('>')).toBe('>') 17 | expect(decodeHtmlBrowser('&gt;')).toBe('>') 18 | 19 | expect(decodeHtmlBrowser(' ')).toBe('\u00a0') 20 | expect(decodeHtmlBrowser('"')).toBe('"') 21 | expect(decodeHtmlBrowser(''')).toBe("'") 22 | 23 | expect(decodeHtmlBrowser('É')).toBe('\u00c9') 24 | expect(decodeHtmlBrowser('É')).toBe('\u00c9') 25 | expect(decodeHtmlBrowser('É')).toBe('\u00c9') 26 | 27 | // #3001 html tags inside attribute values 28 | expect(decodeHtmlBrowser('Text', true)).toBe( 29 | 'Text' 30 | ) 31 | expect(decodeHtmlBrowser('&', true)).toBe( 32 | '&' 33 | ) 34 | expect( 35 | decodeHtmlBrowser( 36 | '<strong>&</strong>', 37 | true 38 | ) 39 | ).toBe('&') 40 | }) 41 | }) 42 | -------------------------------------------------------------------------------- /packages/runtime-core/src/profiling.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-restricted-globals */ 2 | import { ComponentInternalInstance, formatComponentName } from './component' 3 | import { devtoolsPerfEnd, devtoolsPerfStart } from './devtools' 4 | 5 | let supported: boolean 6 | let perf: Performance 7 | 8 | export function startMeasure( 9 | instance: ComponentInternalInstance, 10 | type: string 11 | ) { 12 | if (instance.appContext.config.performance && isSupported()) { 13 | perf.mark(`vue-${type}-${instance.uid}`) 14 | } 15 | 16 | if (__DEV__ || __FEATURE_PROD_DEVTOOLS__) { 17 | devtoolsPerfStart(instance, type, isSupported() ? perf.now() : Date.now()) 18 | } 19 | } 20 | 21 | export function endMeasure(instance: ComponentInternalInstance, type: string) { 22 | if (instance.appContext.config.performance && isSupported()) { 23 | const startTag = `vue-${type}-${instance.uid}` 24 | const endTag = startTag + `:end` 25 | perf.mark(endTag) 26 | perf.measure( 27 | `<${formatComponentName(instance, instance.type)}> ${type}`, 28 | startTag, 29 | endTag 30 | ) 31 | perf.clearMarks(startTag) 32 | perf.clearMarks(endTag) 33 | } 34 | 35 | if (__DEV__ || __FEATURE_PROD_DEVTOOLS__) { 36 | devtoolsPerfEnd(instance, type, isSupported() ? perf.now() : Date.now()) 37 | } 38 | } 39 | 40 | function isSupported() { 41 | if (supported !== undefined) { 42 | return supported 43 | } 44 | if (typeof window !== 'undefined' && window.performance) { 45 | supported = true 46 | perf = window.performance 47 | } else { 48 | supported = false 49 | } 50 | return supported 51 | } 52 | -------------------------------------------------------------------------------- /packages/compiler-ssr/src/transforms/ssrVFor.ts: -------------------------------------------------------------------------------- 1 | import { 2 | createStructuralDirectiveTransform, 3 | ForNode, 4 | processFor, 5 | createCallExpression, 6 | createFunctionExpression, 7 | createForLoopParams, 8 | NodeTypes 9 | } from '@vue/compiler-dom' 10 | import { 11 | SSRTransformContext, 12 | processChildrenAsStatement 13 | } from '../ssrCodegenTransform' 14 | import { SSR_RENDER_LIST } from '../runtimeHelpers' 15 | 16 | // Plugin for the first transform pass, which simply constructs the AST node 17 | export const ssrTransformFor = createStructuralDirectiveTransform( 18 | 'for', 19 | processFor 20 | ) 21 | 22 | // This is called during the 2nd transform pass to construct the SSR-specific 23 | // codegen nodes. 24 | export function ssrProcessFor( 25 | node: ForNode, 26 | context: SSRTransformContext, 27 | disableNestedFragments = false 28 | ) { 29 | const needFragmentWrapper = 30 | !disableNestedFragments && 31 | (node.children.length !== 1 || node.children[0].type !== NodeTypes.ELEMENT) 32 | const renderLoop = createFunctionExpression( 33 | createForLoopParams(node.parseResult) 34 | ) 35 | renderLoop.body = processChildrenAsStatement( 36 | node.children, 37 | context, 38 | needFragmentWrapper 39 | ) 40 | // v-for always renders a fragment unless explicitly disabled 41 | if (!disableNestedFragments) { 42 | context.pushStringPart(``) 43 | } 44 | context.pushStatement( 45 | createCallExpression(context.helper(SSR_RENDER_LIST), [ 46 | node.source, 47 | renderLoop 48 | ]) 49 | ) 50 | if (!disableNestedFragments) { 51 | context.pushStringPart(``) 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /packages/compiler-dom/src/transforms/warnTransitionChildren.ts: -------------------------------------------------------------------------------- 1 | import { 2 | NodeTransform, 3 | NodeTypes, 4 | ElementTypes, 5 | ComponentNode, 6 | IfBranchNode 7 | } from '@vue/compiler-core' 8 | import { TRANSITION } from '../runtimeHelpers' 9 | import { createDOMCompilerError, DOMErrorCodes } from '../errors' 10 | 11 | export const warnTransitionChildren: NodeTransform = (node, context) => { 12 | if ( 13 | node.type === NodeTypes.ELEMENT && 14 | node.tagType === ElementTypes.COMPONENT 15 | ) { 16 | const component = context.isBuiltInComponent(node.tag) 17 | if (component === TRANSITION) { 18 | return () => { 19 | if (node.children.length && hasMultipleChildren(node)) { 20 | context.onError( 21 | createDOMCompilerError( 22 | DOMErrorCodes.X_TRANSITION_INVALID_CHILDREN, 23 | { 24 | start: node.children[0].loc.start, 25 | end: node.children[node.children.length - 1].loc.end, 26 | source: '' 27 | } 28 | ) 29 | ) 30 | } 31 | } 32 | } 33 | } 34 | } 35 | 36 | function hasMultipleChildren(node: ComponentNode | IfBranchNode): boolean { 37 | // #1352 filter out potential comment nodes. 38 | const children = (node.children = node.children.filter( 39 | c => 40 | c.type !== NodeTypes.COMMENT && 41 | !(c.type === NodeTypes.TEXT && !c.content.trim()) 42 | )) 43 | const child = children[0] 44 | return ( 45 | children.length !== 1 || 46 | child.type === NodeTypes.FOR || 47 | (child.type === NodeTypes.IF && child.branches.some(hasMultipleChildren)) 48 | ) 49 | } 50 | -------------------------------------------------------------------------------- /packages/sfc-playground/src/App.vue: -------------------------------------------------------------------------------- 1 | 29 | 30 | 42 | 43 | 65 | -------------------------------------------------------------------------------- /packages/runtime-core/__tests__/rendererElement.spec.ts: -------------------------------------------------------------------------------- 1 | import { 2 | h, 3 | render, 4 | nodeOps, 5 | TestElement, 6 | serializeInner as inner 7 | } from '@vue/runtime-test' 8 | 9 | describe('renderer: element', () => { 10 | let root: TestElement 11 | 12 | beforeEach(() => { 13 | root = nodeOps.createElement('div') 14 | }) 15 | 16 | it('should create an element', () => { 17 | render(h('div'), root) 18 | expect(inner(root)).toBe('
') 19 | }) 20 | 21 | it('should create an element with props', () => { 22 | render(h('div', { id: 'foo', class: 'bar' }), root) 23 | expect(inner(root)).toBe('
') 24 | }) 25 | 26 | it('should create an element with direct text children', () => { 27 | render(h('div', ['foo', ' ', 'bar']), root) 28 | expect(inner(root)).toBe('
foo bar
') 29 | }) 30 | 31 | it('should create an element with direct text children and props', () => { 32 | render(h('div', { id: 'foo' }, ['bar']), root) 33 | expect(inner(root)).toBe('
bar
') 34 | }) 35 | 36 | it('should update an element tag which is already mounted', () => { 37 | render(h('div', ['foo']), root) 38 | expect(inner(root)).toBe('
foo
') 39 | 40 | render(h('span', ['foo']), root) 41 | expect(inner(root)).toBe('foo') 42 | }) 43 | 44 | it('should update element props which is already mounted', () => { 45 | render(h('div', { id: 'bar' }, ['foo']), root) 46 | expect(inner(root)).toBe('
foo
') 47 | 48 | render(h('div', { id: 'baz', class: 'bar' }, ['foo']), root) 49 | expect(inner(root)).toBe('
foo
') 50 | }) 51 | }) 52 | -------------------------------------------------------------------------------- /packages/shared/src/looseEqual.ts: -------------------------------------------------------------------------------- 1 | import { isArray, isDate, isObject } from './' 2 | 3 | function looseCompareArrays(a: any[], b: any[]) { 4 | if (a.length !== b.length) return false 5 | let equal = true 6 | for (let i = 0; equal && i < a.length; i++) { 7 | equal = looseEqual(a[i], b[i]) 8 | } 9 | return equal 10 | } 11 | 12 | export function looseEqual(a: any, b: any): boolean { 13 | if (a === b) return true 14 | let aValidType = isDate(a) 15 | let bValidType = isDate(b) 16 | if (aValidType || bValidType) { 17 | return aValidType && bValidType ? a.getTime() === b.getTime() : false 18 | } 19 | aValidType = isArray(a) 20 | bValidType = isArray(b) 21 | if (aValidType || bValidType) { 22 | return aValidType && bValidType ? looseCompareArrays(a, b) : false 23 | } 24 | aValidType = isObject(a) 25 | bValidType = isObject(b) 26 | if (aValidType || bValidType) { 27 | /* istanbul ignore if: this if will probably never be called */ 28 | if (!aValidType || !bValidType) { 29 | return false 30 | } 31 | const aKeysCount = Object.keys(a).length 32 | const bKeysCount = Object.keys(b).length 33 | if (aKeysCount !== bKeysCount) { 34 | return false 35 | } 36 | for (const key in a) { 37 | const aHasKey = a.hasOwnProperty(key) 38 | const bHasKey = b.hasOwnProperty(key) 39 | if ( 40 | (aHasKey && !bHasKey) || 41 | (!aHasKey && bHasKey) || 42 | !looseEqual(a[key], b[key]) 43 | ) { 44 | return false 45 | } 46 | } 47 | } 48 | return String(a) === String(b) 49 | } 50 | 51 | export function looseIndexOf(arr: any[], val: any): number { 52 | return arr.findIndex(item => looseEqual(item, val)) 53 | } 54 | -------------------------------------------------------------------------------- /packages/compiler-sfc/src/index.ts: -------------------------------------------------------------------------------- 1 | // API 2 | export { parse } from './parse' 3 | export { compileTemplate } from './compileTemplate' 4 | export { compileStyle, compileStyleAsync } from './compileStyle' 5 | export { compileScript } from './compileScript' 6 | export { rewriteDefault } from './rewriteDefault' 7 | export { 8 | shouldTransform as shouldTransformRef, 9 | transform as transformRef, 10 | transformAST as transformRefAST 11 | } from '@vue/reactivity-transform' 12 | 13 | // Utilities 14 | export { parse as babelParse } from '@babel/parser' 15 | import MagicString from 'magic-string' 16 | export { MagicString } 17 | // technically internal but we want it in @vue/repl, cast it as any to avoid 18 | // relying on estree types 19 | import { walk as _walk } from 'estree-walker' 20 | export const walk = _walk as any 21 | export { 22 | generateCodeFrame, 23 | walkIdentifiers, 24 | extractIdentifiers, 25 | isInDestructureAssignment, 26 | isStaticProperty 27 | } from '@vue/compiler-core' 28 | 29 | // Types 30 | export { 31 | SFCParseOptions, 32 | SFCDescriptor, 33 | SFCBlock, 34 | SFCTemplateBlock, 35 | SFCScriptBlock, 36 | SFCStyleBlock, 37 | SFCParseResult 38 | } from './parse' 39 | export { 40 | TemplateCompiler, 41 | SFCTemplateCompileOptions, 42 | SFCTemplateCompileResults 43 | } from './compileTemplate' 44 | export { 45 | SFCStyleCompileOptions, 46 | SFCAsyncStyleCompileOptions, 47 | SFCStyleCompileResults 48 | } from './compileStyle' 49 | export { SFCScriptCompileOptions } from './compileScript' 50 | export { AssetURLOptions, AssetURLTagConfig } from './templateTransformAssetUrl' 51 | export { 52 | CompilerOptions, 53 | CompilerError, 54 | BindingMetadata 55 | } from '@vue/compiler-core' 56 | -------------------------------------------------------------------------------- /packages/compiler-sfc/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@vue/compiler-sfc", 3 | "version": "3.2.31", 4 | "description": "@vue/compiler-sfc", 5 | "main": "dist/compiler-sfc.cjs.js", 6 | "module": "dist/compiler-sfc.esm-browser.js", 7 | "types": "dist/compiler-sfc.d.ts", 8 | "files": [ 9 | "dist" 10 | ], 11 | "buildOptions": { 12 | "name": "VueCompilerSFC", 13 | "formats": [ 14 | "cjs", 15 | "esm-browser" 16 | ], 17 | "prod": false, 18 | "enableNonBrowserBranches": true 19 | }, 20 | "repository": { 21 | "type": "git", 22 | "url": "git+https://github.com/vuejs/core.git", 23 | "directory": "packages/compiler-sfc" 24 | }, 25 | "keywords": [ 26 | "vue" 27 | ], 28 | "author": "Evan You", 29 | "license": "MIT", 30 | "bugs": { 31 | "url": "https://github.com/vuejs/core/issues" 32 | }, 33 | "homepage": "https://github.com/vuejs/core/tree/main/packages/compiler-sfc#readme", 34 | "dependencies": { 35 | "@babel/parser": "^7.16.4", 36 | "@vue/compiler-core": "3.2.31", 37 | "@vue/compiler-dom": "3.2.31", 38 | "@vue/compiler-ssr": "3.2.31", 39 | "@vue/reactivity-transform": "3.2.31", 40 | "@vue/shared": "3.2.31", 41 | "estree-walker": "^2.0.2", 42 | "magic-string": "^0.25.7", 43 | "source-map": "^0.6.1", 44 | "postcss": "^8.1.10" 45 | }, 46 | "devDependencies": { 47 | "@types/estree": "^0.0.48", 48 | "@babel/types": "^7.16.0", 49 | "@types/lru-cache": "^5.1.0", 50 | "pug": "^3.0.1", 51 | "sass": "^1.26.9", 52 | "@vue/consolidate": "^0.17.3", 53 | "hash-sum": "^2.0.0", 54 | "lru-cache": "^5.1.1", 55 | "merge-source-map": "^1.1.0", 56 | "postcss-modules": "^4.0.0", 57 | "postcss-selector-parser": "^6.0.4" 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /packages/runtime-core/__tests__/helpers/createSlots.spec.ts: -------------------------------------------------------------------------------- 1 | import { Slot } from '../../src/componentSlots' 2 | import { createSlots } from '../../src/helpers/createSlots' 3 | 4 | describe('createSlot', () => { 5 | const slot = () => [] 6 | let record: Record 7 | 8 | beforeEach(() => { 9 | record = {} 10 | }) 11 | 12 | it('should return a slot', () => { 13 | const dynamicSlot = [{ name: 'descriptor', fn: slot }] 14 | 15 | const actual = createSlots(record, dynamicSlot) 16 | 17 | expect(actual).toEqual({ descriptor: slot }) 18 | }) 19 | 20 | it('should add all slots to the record', () => { 21 | const dynamicSlot = [ 22 | { name: 'descriptor', fn: slot }, 23 | { name: 'descriptor2', fn: slot } 24 | ] 25 | 26 | const actual = createSlots(record, dynamicSlot) 27 | 28 | expect(actual).toEqual({ descriptor: slot, descriptor2: slot }) 29 | }) 30 | 31 | it('should add slot to the record when given slot is an array', () => { 32 | const dynamicSlot = [ 33 | { name: 'descriptor', fn: slot }, 34 | [{ name: 'descriptor2', fn: slot }] 35 | ] 36 | 37 | const actual = createSlots(record, dynamicSlot) 38 | 39 | expect(actual).toEqual({ descriptor: slot, descriptor2: slot }) 40 | }) 41 | 42 | it('should add each slot to the record when given slot is an array', () => { 43 | const dynamicSlot = [ 44 | { name: 'descriptor', fn: slot }, 45 | [ 46 | { name: 'descriptor2', fn: slot }, 47 | { name: 'descriptor3', fn: slot } 48 | ] 49 | ] 50 | 51 | const actual = createSlots(record, dynamicSlot) 52 | 53 | expect(actual).toEqual({ 54 | descriptor: slot, 55 | descriptor2: slot, 56 | descriptor3: slot 57 | }) 58 | }) 59 | }) 60 | -------------------------------------------------------------------------------- /packages/template-explorer/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; 4 | --bg: #1D1F21; 5 | --border: #333; 6 | } 7 | 8 | #header { 9 | position: absolute; 10 | top: 0; 11 | left: 0; 12 | right: 0; 13 | height: 60px; 14 | box-sizing: border-box; 15 | background-color: var(--bg); 16 | border-bottom: 1px solid var(--border); 17 | padding: 0.3em 1.6em; 18 | color: #fff; 19 | z-index: 1; 20 | } 21 | 22 | h1 { 23 | font-size: 18px; 24 | display: inline-block; 25 | margin-right: 15px; 26 | } 27 | 28 | #options-wrapper { 29 | position: absolute; 30 | top: 20px; 31 | right: 10px; 32 | } 33 | 34 | #options-wrapper:hover #options { 35 | display: block; 36 | } 37 | 38 | #options-label { 39 | cursor: pointer; 40 | text-align: right; 41 | padding-right: 10px; 42 | font-weight: bold; 43 | } 44 | 45 | #options { 46 | display: none; 47 | margin-top: 15px; 48 | list-style-type: none; 49 | background-color: var(--bg); 50 | border: 1px solid var(--border); 51 | padding: 15px 30px; 52 | } 53 | 54 | #options li { 55 | margin: 8px 0; 56 | } 57 | 58 | #header a { 59 | font-weight: 600; 60 | color: rgb(101, 163, 221); 61 | } 62 | 63 | #header .label { 64 | font-weight: bold; 65 | } 66 | 67 | #header input { 68 | margin-right: 6px; 69 | } 70 | 71 | #header label { 72 | color: #999; 73 | } 74 | 75 | .editor { 76 | position: absolute; 77 | top: 60px; 78 | bottom: 0; 79 | box-sizing: border-box; 80 | } 81 | 82 | #source { 83 | left: 0; 84 | width: 45%; 85 | } 86 | 87 | #output { 88 | left: 45%; 89 | width: 55%; 90 | } 91 | 92 | .highlight { 93 | background-color: rgba(46, 120, 190, 0.5); 94 | } 95 | -------------------------------------------------------------------------------- /packages/runtime-test/src/serialize.ts: -------------------------------------------------------------------------------- 1 | import { 2 | TestElement, 3 | TestNode, 4 | NodeTypes, 5 | TestText, 6 | TestComment 7 | } from './nodeOps' 8 | import { isOn } from '@vue/shared' 9 | 10 | export function serialize( 11 | node: TestNode, 12 | indent: number = 0, 13 | depth: number = 0 14 | ): string { 15 | if (node.type === NodeTypes.ELEMENT) { 16 | return serializeElement(node, indent, depth) 17 | } else { 18 | return serializeText(node, indent, depth) 19 | } 20 | } 21 | 22 | export function serializeInner( 23 | node: TestElement, 24 | indent: number = 0, 25 | depth: number = 0 26 | ) { 27 | const newLine = indent ? `\n` : `` 28 | return node.children.length 29 | ? newLine + 30 | node.children.map(c => serialize(c, indent, depth + 1)).join(newLine) + 31 | newLine 32 | : `` 33 | } 34 | 35 | function serializeElement( 36 | node: TestElement, 37 | indent: number, 38 | depth: number 39 | ): string { 40 | const props = Object.keys(node.props) 41 | .map(key => { 42 | const value = node.props[key] 43 | return isOn(key) || value == null 44 | ? `` 45 | : value === `` 46 | ? key 47 | : `${key}=${JSON.stringify(value)}` 48 | }) 49 | .filter(Boolean) 50 | .join(' ') 51 | const padding = indent ? ` `.repeat(indent).repeat(depth) : `` 52 | return ( 53 | `${padding}<${node.tag}${props ? ` ${props}` : ``}>` + 54 | `${serializeInner(node, indent, depth)}` + 55 | `${padding}` 56 | ) 57 | } 58 | 59 | function serializeText( 60 | node: TestText | TestComment, 61 | indent: number, 62 | depth: number 63 | ): string { 64 | const padding = indent ? ` `.repeat(indent).repeat(depth) : `` 65 | return ( 66 | padding + 67 | (node.type === NodeTypes.COMMENT ? `` : node.text) 68 | ) 69 | } 70 | -------------------------------------------------------------------------------- /packages/runtime-core/src/compat/customDirective.ts: -------------------------------------------------------------------------------- 1 | import { isArray } from '@vue/shared' 2 | import { ComponentInternalInstance } from '../component' 3 | import { ObjectDirective, DirectiveHook } from '../directives' 4 | import { softAssertCompatEnabled, DeprecationTypes } from './compatConfig' 5 | 6 | export interface LegacyDirective { 7 | bind?: DirectiveHook 8 | inserted?: DirectiveHook 9 | update?: DirectiveHook 10 | componentUpdated?: DirectiveHook 11 | unbind?: DirectiveHook 12 | } 13 | 14 | const legacyDirectiveHookMap: Partial< 15 | Record< 16 | keyof ObjectDirective, 17 | keyof LegacyDirective | (keyof LegacyDirective)[] 18 | > 19 | > = { 20 | beforeMount: 'bind', 21 | mounted: 'inserted', 22 | updated: ['update', 'componentUpdated'], 23 | unmounted: 'unbind' 24 | } 25 | 26 | export function mapCompatDirectiveHook( 27 | name: keyof ObjectDirective, 28 | dir: ObjectDirective & LegacyDirective, 29 | instance: ComponentInternalInstance | null 30 | ): DirectiveHook | DirectiveHook[] | undefined { 31 | const mappedName = legacyDirectiveHookMap[name] 32 | if (mappedName) { 33 | if (isArray(mappedName)) { 34 | const hook: DirectiveHook[] = [] 35 | mappedName.forEach(mapped => { 36 | const mappedHook = dir[mapped] 37 | if (mappedHook) { 38 | softAssertCompatEnabled( 39 | DeprecationTypes.CUSTOM_DIR, 40 | instance, 41 | mapped, 42 | name 43 | ) 44 | hook.push(mappedHook) 45 | } 46 | }) 47 | return hook.length ? hook : undefined 48 | } else { 49 | if (dir[mappedName]) { 50 | softAssertCompatEnabled( 51 | DeprecationTypes.CUSTOM_DIR, 52 | instance, 53 | mappedName, 54 | name 55 | ) 56 | } 57 | return dir[mappedName] 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /packages/compiler-ssr/__tests__/ssrSuspense.spec.ts: -------------------------------------------------------------------------------- 1 | import { compile } from '../src' 2 | 3 | describe('ssr compile: suspense', () => { 4 | test('implicit default', () => { 5 | expect(compile(``).code).toMatchInlineSnapshot(` 6 | "const { resolveComponent: _resolveComponent, withCtx: _withCtx } = require(\\"vue\\") 7 | const { ssrRenderComponent: _ssrRenderComponent, ssrRenderSuspense: _ssrRenderSuspense } = require(\\"vue/server-renderer\\") 8 | 9 | return function ssrRender(_ctx, _push, _parent, _attrs) { 10 | const _component_foo = _resolveComponent(\\"foo\\") 11 | 12 | _ssrRenderSuspense(_push, { 13 | default: () => { 14 | _push(_ssrRenderComponent(_component_foo, null, null, _parent)) 15 | }, 16 | _: 1 /* STABLE */ 17 | }) 18 | }" 19 | `) 20 | }) 21 | 22 | test('explicit slots', () => { 23 | expect( 24 | compile(` 25 | 28 | 31 | `).code 32 | ).toMatchInlineSnapshot(` 33 | "const { resolveComponent: _resolveComponent, withCtx: _withCtx } = require(\\"vue\\") 34 | const { ssrRenderComponent: _ssrRenderComponent, ssrRenderSuspense: _ssrRenderSuspense } = require(\\"vue/server-renderer\\") 35 | 36 | return function ssrRender(_ctx, _push, _parent, _attrs) { 37 | const _component_foo = _resolveComponent(\\"foo\\") 38 | 39 | _ssrRenderSuspense(_push, { 40 | default: () => { 41 | _push(_ssrRenderComponent(_component_foo, null, null, _parent)) 42 | }, 43 | fallback: () => { 44 | _push(\` loading... \`) 45 | }, 46 | _: 1 /* STABLE */ 47 | }) 48 | }" 49 | `) 50 | }) 51 | }) 52 | -------------------------------------------------------------------------------- /packages/runtime-core/__tests__/helpers/renderList.spec.ts: -------------------------------------------------------------------------------- 1 | import { renderList } from '../../src/helpers/renderList' 2 | 3 | describe('renderList', () => { 4 | it('should render items in an array', () => { 5 | expect( 6 | renderList(['1', '2', '3'], (item, index) => `node ${index}: ${item}`) 7 | ).toEqual(['node 0: 1', 'node 1: 2', 'node 2: 3']) 8 | }) 9 | 10 | it('should render characters of a string', () => { 11 | expect( 12 | renderList('123', (item, index) => `node ${index}: ${item}`) 13 | ).toEqual(['node 0: 1', 'node 1: 2', 'node 2: 3']) 14 | }) 15 | 16 | it('should render integers 1 through N when given a number N', () => { 17 | expect(renderList(3, (item, index) => `node ${index}: ${item}`)).toEqual([ 18 | 'node 0: 1', 19 | 'node 1: 2', 20 | 'node 2: 3' 21 | ]) 22 | }) 23 | 24 | it('should warn when given a non-integer N', () => { 25 | renderList(3.1, () => {}) 26 | expect( 27 | `The v-for range expect an integer value but got 3.1.` 28 | ).toHaveBeenWarned() 29 | }) 30 | 31 | it('should render properties in an object', () => { 32 | expect( 33 | renderList( 34 | { a: 1, b: 2, c: 3 }, 35 | (item, key, index) => `node ${index}/${key}: ${item}` 36 | ) 37 | ).toEqual(['node 0/a: 1', 'node 1/b: 2', 'node 2/c: 3']) 38 | }) 39 | 40 | it('should render an item for entry in an iterable', () => { 41 | const iterable = function* () { 42 | yield 1 43 | yield 2 44 | yield 3 45 | } 46 | 47 | expect( 48 | renderList(iterable(), (item, index) => `node ${index}: ${item}`) 49 | ).toEqual(['node 0: 1', 'node 1: 2', 'node 2: 3']) 50 | }) 51 | 52 | it('should return empty array when source is undefined', () => { 53 | expect( 54 | renderList(undefined, (item, index) => `node ${index}: ${item}`) 55 | ).toEqual([]) 56 | }) 57 | }) 58 | -------------------------------------------------------------------------------- /packages/compiler-dom/__tests__/transforms/__snapshots__/stringifyStatic.spec.ts.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`stringify static html should bail on bindings that are hoisted but not stringifiable 1`] = ` 4 | "const { createElementVNode: _createElementVNode, openBlock: _openBlock, createElementBlock: _createElementBlock } = Vue 5 | 6 | const _hoisted_1 = /*#__PURE__*/_createElementVNode(\\"div\\", null, [ 7 | /*#__PURE__*/_createElementVNode(\\"span\\", { class: \\"foo\\" }, \\"foo\\"), 8 | /*#__PURE__*/_createElementVNode(\\"span\\", { class: \\"foo\\" }, \\"foo\\"), 9 | /*#__PURE__*/_createElementVNode(\\"span\\", { class: \\"foo\\" }, \\"foo\\"), 10 | /*#__PURE__*/_createElementVNode(\\"span\\", { class: \\"foo\\" }, \\"foo\\"), 11 | /*#__PURE__*/_createElementVNode(\\"span\\", { class: \\"foo\\" }, \\"foo\\"), 12 | /*#__PURE__*/_createElementVNode(\\"img\\", { src: _imports_0_ }) 13 | ], -1 /* HOISTED */) 14 | const _hoisted_2 = [ 15 | _hoisted_1 16 | ] 17 | 18 | return function render(_ctx, _cache) { 19 | return (_openBlock(), _createElementBlock(\\"div\\", null, _hoisted_2)) 20 | }" 21 | `; 22 | 23 | exports[`stringify static html should work with bindings that are non-static but stringifiable 1`] = ` 24 | "const { createElementVNode: _createElementVNode, createStaticVNode: _createStaticVNode, openBlock: _openBlock, createElementBlock: _createElementBlock } = Vue 25 | 26 | const _hoisted_1 = /*#__PURE__*/_createStaticVNode(\\"
foofoofoofoofoo
\\", 1) 27 | const _hoisted_2 = [ 28 | _hoisted_1 29 | ] 30 | 31 | return function render(_ctx, _cache) { 32 | return (_openBlock(), _createElementBlock(\\"div\\", null, _hoisted_2)) 33 | }" 34 | `; 35 | -------------------------------------------------------------------------------- /packages/compiler-ssr/src/transforms/ssrInjectCssVars.ts: -------------------------------------------------------------------------------- 1 | import { 2 | NodeTransform, 3 | NodeTypes, 4 | ElementTypes, 5 | locStub, 6 | createSimpleExpression, 7 | RootNode, 8 | TemplateChildNode, 9 | findDir, 10 | isBuiltInType 11 | } from '@vue/compiler-dom' 12 | 13 | export const ssrInjectCssVars: NodeTransform = (node, context) => { 14 | if (!context.ssrCssVars) { 15 | return 16 | } 17 | 18 | // _cssVars is initialized once per render function 19 | // the code is injected in ssrCodegenTransform when creating the 20 | // ssr transform context 21 | if (node.type === NodeTypes.ROOT) { 22 | context.identifiers._cssVars = 1 23 | } 24 | 25 | const parent = context.parent 26 | if (!parent || parent.type !== NodeTypes.ROOT) { 27 | return 28 | } 29 | 30 | if (node.type === NodeTypes.IF_BRANCH) { 31 | for (const child of node.children) { 32 | injectCssVars(child) 33 | } 34 | } else { 35 | injectCssVars(node) 36 | } 37 | } 38 | 39 | function injectCssVars(node: RootNode | TemplateChildNode) { 40 | if ( 41 | node.type === NodeTypes.ELEMENT && 42 | (node.tagType === ElementTypes.ELEMENT || 43 | node.tagType === ElementTypes.COMPONENT) && 44 | !findDir(node, 'for') 45 | ) { 46 | if (isBuiltInType(node.tag, 'Suspense')) { 47 | for (const child of node.children) { 48 | if ( 49 | child.type === NodeTypes.ELEMENT && 50 | child.tagType === ElementTypes.TEMPLATE 51 | ) { 52 | // suspense slot 53 | child.children.forEach(injectCssVars) 54 | } else { 55 | injectCssVars(child) 56 | } 57 | } 58 | } else { 59 | node.props.push({ 60 | type: NodeTypes.DIRECTIVE, 61 | name: 'bind', 62 | arg: undefined, 63 | exp: createSimpleExpression(`_cssVars`, false), 64 | modifiers: [], 65 | loc: locStub 66 | }) 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /packages/server-renderer/__tests__/webStream.spec.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @jest-environment node 3 | */ 4 | 5 | import { createApp, h, defineAsyncComponent } from 'vue' 6 | import { ReadableStream, TransformStream } from 'stream/web' 7 | import { pipeToWebWritable, renderToWebStream } from '../src' 8 | 9 | beforeEach(() => { 10 | global.ReadableStream = ReadableStream 11 | }) 12 | 13 | afterEach(() => { 14 | // @ts-ignore 15 | delete global.ReadableStream 16 | }) 17 | 18 | test('renderToWebStream', async () => { 19 | const Async = defineAsyncComponent(() => 20 | Promise.resolve({ 21 | render: () => h('div', 'async') 22 | }) 23 | ) 24 | const App = { 25 | render: () => [h('div', 'parent'), h(Async)] 26 | } 27 | 28 | const stream = renderToWebStream(createApp(App)) 29 | 30 | const reader = stream.getReader() 31 | const decoder = new TextDecoder() 32 | 33 | let res = '' 34 | await reader.read().then(function read({ done, value }): any { 35 | if (!done) { 36 | res += decoder.decode(value) 37 | return reader.read().then(read) 38 | } 39 | }) 40 | 41 | expect(res).toBe(`
parent
async
`) 42 | }) 43 | 44 | test('pipeToWebWritable', async () => { 45 | const Async = defineAsyncComponent(() => 46 | Promise.resolve({ 47 | render: () => h('div', 'async') 48 | }) 49 | ) 50 | const App = { 51 | render: () => [h('div', 'parent'), h(Async)] 52 | } 53 | 54 | const { readable, writable } = new TransformStream() 55 | pipeToWebWritable(createApp(App), {}, writable) 56 | 57 | const reader = readable.getReader() 58 | const decoder = new TextDecoder() 59 | 60 | let res = '' 61 | await reader.read().then(function read({ done, value }): any { 62 | if (!done) { 63 | res += decoder.decode(value) 64 | return reader.read().then(read) 65 | } 66 | }) 67 | 68 | expect(res).toBe(`
parent
async
`) 69 | }) 70 | -------------------------------------------------------------------------------- /packages/compiler-ssr/src/transforms/ssrInjectFallthroughAttrs.ts: -------------------------------------------------------------------------------- 1 | import { 2 | NodeTransform, 3 | NodeTypes, 4 | ElementTypes, 5 | locStub, 6 | createSimpleExpression, 7 | RootNode, 8 | TemplateChildNode, 9 | ParentNode, 10 | findDir, 11 | isBuiltInType 12 | } from '@vue/compiler-dom' 13 | 14 | const hasSingleChild = (node: ParentNode): boolean => 15 | node.children.filter(n => n.type !== NodeTypes.COMMENT).length === 1 16 | 17 | export const ssrInjectFallthroughAttrs: NodeTransform = (node, context) => { 18 | // _attrs is provided as a function argument. 19 | // mark it as a known identifier so that it doesn't get prefixed by 20 | // transformExpression. 21 | if (node.type === NodeTypes.ROOT) { 22 | context.identifiers._attrs = 1 23 | } 24 | 25 | if ( 26 | node.type === NodeTypes.ELEMENT && 27 | node.tagType === ElementTypes.COMPONENT && 28 | (isBuiltInType(node.tag, 'Transition') || 29 | isBuiltInType(node.tag, 'KeepAlive')) 30 | ) { 31 | if (hasSingleChild(node)) { 32 | injectFallthroughAttrs(node.children[0]) 33 | } 34 | return 35 | } 36 | 37 | const parent = context.parent 38 | if (!parent || parent.type !== NodeTypes.ROOT) { 39 | return 40 | } 41 | 42 | if (node.type === NodeTypes.IF_BRANCH && hasSingleChild(node)) { 43 | injectFallthroughAttrs(node.children[0]) 44 | } else if (hasSingleChild(parent)) { 45 | injectFallthroughAttrs(node) 46 | } 47 | } 48 | 49 | function injectFallthroughAttrs(node: RootNode | TemplateChildNode) { 50 | if ( 51 | node.type === NodeTypes.ELEMENT && 52 | (node.tagType === ElementTypes.ELEMENT || 53 | node.tagType === ElementTypes.COMPONENT) && 54 | !findDir(node, 'for') 55 | ) { 56 | node.props.push({ 57 | type: NodeTypes.DIRECTIVE, 58 | name: 'bind', 59 | arg: undefined, 60 | exp: createSimpleExpression(`_attrs`, false), 61 | modifiers: [], 62 | loc: locStub 63 | }) 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /packages/server-renderer/__tests__/ssrComputed.spec.ts: -------------------------------------------------------------------------------- 1 | import { createSSRApp, defineComponent, h, computed, reactive } from 'vue' 2 | import { renderToString } from '../src/renderToString' 3 | 4 | // #5208 reported memory leak of keeping computed alive during SSR 5 | // so we made computed properties created during SSR non-reactive in 6 | // https://github.com/vuejs/core/commit/f4f0966b33863ac0fca6a20cf9e8ddfbb311ae87 7 | // However, the default caching leads to #5300 which is tested below. 8 | // In Vue 2, computed properties are simple getters during SSR - this can be 9 | // inefficient if an expensive computed is accessed multiple times during render, 10 | // but because of potential mutations, we cannot cache it until we enter the 11 | // render phase (where no mutations can happen anymore) 12 | test('computed reactivity during SSR', async () => { 13 | const store = { 14 | // initial state could be hydrated 15 | state: reactive({ items: null }) as any, 16 | 17 | // pretend to fetch some data from an api 18 | async fetchData() { 19 | this.state.items = ['hello', 'world'] 20 | } 21 | } 22 | 23 | const getterSpy = jest.fn() 24 | 25 | const App = defineComponent(async () => { 26 | const msg = computed(() => { 27 | getterSpy() 28 | return store.state.items?.join(' ') 29 | }) 30 | 31 | // If msg value is falsy then we are either in ssr context or on the client 32 | // and the initial state was not modified/hydrated. 33 | // In both cases we need to fetch data. 34 | if (!msg.value) await store.fetchData() 35 | 36 | expect(msg.value).toBe('hello world') 37 | return () => h('div', null, msg.value + msg.value + msg.value) 38 | }) 39 | 40 | const app = createSSRApp(App) 41 | 42 | const html = await renderToString(app) 43 | expect(html).toMatch('hello world') 44 | 45 | // should only be called twice since access should be cached 46 | // during the render phase 47 | expect(getterSpy).toHaveBeenCalledTimes(2) 48 | }) 49 | -------------------------------------------------------------------------------- /packages/compiler-ssr/src/transforms/ssrTransformSlotOutlet.ts: -------------------------------------------------------------------------------- 1 | import { 2 | NodeTransform, 3 | isSlotOutlet, 4 | processSlotOutlet, 5 | createCallExpression, 6 | SlotOutletNode, 7 | createFunctionExpression 8 | } from '@vue/compiler-dom' 9 | import { SSR_RENDER_SLOT } from '../runtimeHelpers' 10 | import { 11 | SSRTransformContext, 12 | processChildrenAsStatement 13 | } from '../ssrCodegenTransform' 14 | 15 | export const ssrTransformSlotOutlet: NodeTransform = (node, context) => { 16 | if (isSlotOutlet(node)) { 17 | const { slotName, slotProps } = processSlotOutlet(node, context) 18 | 19 | const args = [ 20 | `_ctx.$slots`, 21 | slotName, 22 | slotProps || `{}`, 23 | // fallback content placeholder. will be replaced in the process phase 24 | `null`, 25 | `_push`, 26 | `_parent` 27 | ] 28 | 29 | // inject slot scope id if current template uses :slotted 30 | if (context.scopeId && context.slotted !== false) { 31 | args.push(`"${context.scopeId}-s"`) 32 | } 33 | 34 | node.ssrCodegenNode = createCallExpression( 35 | context.helper(SSR_RENDER_SLOT), 36 | args 37 | ) 38 | } 39 | } 40 | 41 | export function ssrProcessSlotOutlet( 42 | node: SlotOutletNode, 43 | context: SSRTransformContext 44 | ) { 45 | const renderCall = node.ssrCodegenNode! 46 | 47 | // has fallback content 48 | if (node.children.length) { 49 | const fallbackRenderFn = createFunctionExpression([]) 50 | fallbackRenderFn.body = processChildrenAsStatement(node.children, context) 51 | // _renderSlot(slots, name, props, fallback, ...) 52 | renderCall.arguments[3] = fallbackRenderFn 53 | } 54 | 55 | // Forwarded . Merge slot scope ids 56 | if (context.withSlotScopeId) { 57 | const slotScopeId = renderCall.arguments[6] 58 | renderCall.arguments[6] = slotScopeId 59 | ? `${slotScopeId as string} + _scopeId` 60 | : `_scopeId` 61 | } 62 | 63 | context.pushStatement(node.ssrCodegenNode!) 64 | } 65 | -------------------------------------------------------------------------------- /test-dts/functionalComponent.test-d.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | h, 3 | Text, 4 | FunctionalComponent, 5 | expectError, 6 | expectType, 7 | Component 8 | } from './index' 9 | 10 | // simple function signature 11 | const Foo = (props: { foo: number }) => h(Text, null, props.foo) 12 | 13 | // TSX 14 | expectType() 15 | expectType() 16 | expectType() 17 | // @ts-expect-error 18 | expectError() 19 | // @ts-expect-error 20 | expectError() 21 | // @ts-expect-error 22 | expectError() 23 | 24 | // Explicit signature with props + emits 25 | const Bar: FunctionalComponent< 26 | { foo: number }, 27 | { update: (value: number) => void } 28 | > = (props, { emit }) => { 29 | expectType(props.foo) 30 | 31 | emit('update', 123) 32 | // @ts-expect-error 33 | expectError(emit('nope')) 34 | // @ts-expect-error 35 | expectError(emit('update')) 36 | // @ts-expect-error 37 | expectError(emit('update', 'nope')) 38 | } 39 | 40 | // assigning runtime options 41 | Bar.props = { 42 | foo: Number 43 | } 44 | // @ts-expect-error 45 | expectError((Bar.props = { foo: String })) 46 | 47 | Bar.emits = { 48 | update: value => value > 1 49 | } 50 | // @ts-expect-error 51 | expectError((Bar.emits = { baz: () => void 0 })) 52 | 53 | // TSX 54 | expectType() 55 | // @ts-expect-error 56 | expectError() 57 | // @ts-expect-error 58 | expectError() 59 | // @ts-expect-error 60 | expectError() 61 | 62 | const Baz: FunctionalComponent<{}, string[]> = (props, { emit }) => { 63 | expectType<{}>(props) 64 | expectType<(event: string) => void>(emit) 65 | } 66 | 67 | expectType(Baz) 68 | 69 | const Qux: FunctionalComponent<{}, ['foo', 'bar']> = (props, { emit }) => { 70 | emit('foo') 71 | emit('foo', 1, 2) 72 | emit('bar') 73 | emit('bar', 1, 2) 74 | } 75 | 76 | expectType(Qux) 77 | -------------------------------------------------------------------------------- /packages/server-renderer/__tests__/ssrDynamicComponent.spec.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @jest-environment node 3 | */ 4 | 5 | import { createApp, createVNode } from 'vue' 6 | import { renderToString } from '../src/renderToString' 7 | 8 | describe('ssr: dynamic component', () => { 9 | test('resolved to component', async () => { 10 | expect( 11 | await renderToString( 12 | createApp({ 13 | components: { 14 | one: { 15 | template: `
` 16 | } 17 | }, 18 | template: `slot` 19 | }) 20 | ) 21 | ).toBe(`
slot
`) 22 | }) 23 | 24 | test('resolve to element', async () => { 25 | expect( 26 | await renderToString( 27 | createApp({ 28 | template: `slot` 29 | }) 30 | ) 31 | ).toBe(`

slot

`) 32 | }) 33 | 34 | test('resolve to component vnode', async () => { 35 | const Child = { 36 | props: ['id'], 37 | template: `
{{ id }}
` 38 | } 39 | expect( 40 | await renderToString( 41 | createApp({ 42 | setup() { 43 | return { 44 | vnode: createVNode(Child, { id: 'test' }) 45 | } 46 | }, 47 | template: `slot` 48 | }) 49 | ) 50 | ).toBe(`
testslot
`) 51 | }) 52 | 53 | test('resolve to element vnode', async () => { 54 | expect( 55 | await renderToString( 56 | createApp({ 57 | setup() { 58 | return { 59 | vnode: createVNode('div', { id: 'test' }) 60 | } 61 | }, 62 | template: `slot` 63 | }) 64 | ) 65 | ).toBe(`
slot
`) 66 | }) 67 | }) 68 | -------------------------------------------------------------------------------- /test-dts/tsx.test-d.tsx: -------------------------------------------------------------------------------- 1 | // TSX w/ defineComponent is tested in defineComponent.test-d.tsx 2 | import { 3 | KeepAlive, 4 | Suspense, 5 | Fragment, 6 | Teleport, 7 | expectError, 8 | expectType, 9 | VNode 10 | } from './index' 11 | 12 | expectType(
) 13 | expectType(
) 14 | expectType(
) 15 | expectType() 16 | 17 | // @ts-expect-error style css property validation 18 | expectError(
) 19 | 20 | // allow array styles and nested array styles 21 | expectType(
) 22 | expectType( 23 |
24 | ) 25 | 26 | // @ts-expect-error unknown prop 27 | expectError(
) 28 | 29 | // allow key/ref on arbitrary element 30 | expectType(
) 31 | expectType(
) 32 | 33 | expectType( 34 | { 36 | // infer correct event type 37 | expectType(e.target) 38 | }} 39 | /> 40 | ) 41 | 42 | // built-in types 43 | expectType() 44 | expectType() 45 | 46 | expectType() 47 | expectType() 48 | 49 | // @ts-expect-error 50 | expectError() 51 | // @ts-expect-error 52 | expectError() 53 | 54 | // KeepAlive 55 | expectType() 56 | expectType() 57 | // @ts-expect-error 58 | expectError() 59 | 60 | // Suspense 61 | expectType() 62 | expectType() 63 | expectType( 64 | {}} onFallback={() => {}} onPending={() => {}} /> 65 | ) 66 | // @ts-expect-error 67 | expectError() 68 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: 'ci' 2 | on: 3 | push: 4 | branches: 5 | - '**' 6 | pull_request: 7 | branches: 8 | - main 9 | jobs: 10 | test: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v2 14 | 15 | - name: Install pnpm 16 | uses: pnpm/action-setup@v2.0.1 17 | with: 18 | version: 6.15.1 19 | 20 | - name: Set node version to 16 21 | uses: actions/setup-node@v2 22 | with: 23 | node-version: 16 24 | cache: 'pnpm' 25 | 26 | - run: pnpm install 27 | 28 | - name: Run unit tests 29 | run: pnpm run test 30 | 31 | test-dts: 32 | runs-on: ubuntu-latest 33 | steps: 34 | - uses: actions/checkout@v2 35 | 36 | - name: Install pnpm 37 | uses: pnpm/action-setup@v2.0.1 38 | with: 39 | version: 6.15.1 40 | 41 | - name: Set node version to 16 42 | uses: actions/setup-node@v2 43 | with: 44 | node-version: 16 45 | cache: 'pnpm' 46 | 47 | - run: pnpm install 48 | 49 | - name: Run type declaration tests 50 | run: pnpm run test-dts 51 | 52 | size: 53 | runs-on: ubuntu-latest 54 | env: 55 | CI_JOB_NUMBER: 1 56 | steps: 57 | - uses: actions/checkout@v2 58 | 59 | - name: Install pnpm 60 | uses: pnpm/action-setup@v2.0.1 61 | with: 62 | version: 6.15.1 63 | 64 | - name: Set node version to 16 65 | uses: actions/setup-node@v2 66 | with: 67 | node-version: 16 68 | cache: 'pnpm' 69 | 70 | - run: pnpm install 71 | - run: pnpm run size 72 | 73 | # - name: Check build size 74 | # uses: posva/size-check-action@v1.1.2 75 | # with: 76 | # github_token: ${{ secrets.GITHUB_TOKEN }} 77 | # build_script: size 78 | # files: packages/vue/dist/vue.global.prod.js packages/runtime-dom/dist/runtime-dom.global.prod.js packages/size-check/dist/index.js 79 | -------------------------------------------------------------------------------- /packages/runtime-core/src/compat/componentAsync.ts: -------------------------------------------------------------------------------- 1 | import { isArray, isObject, isPromise } from '@vue/shared' 2 | import { defineAsyncComponent } from '../apiAsyncComponent' 3 | import { Component } from '../component' 4 | import { isVNode } from '../vnode' 5 | 6 | interface LegacyAsyncOptions { 7 | component: Promise 8 | loading?: Component 9 | error?: Component 10 | delay?: number 11 | timeout?: number 12 | } 13 | 14 | type LegacyAsyncReturnValue = Promise | LegacyAsyncOptions 15 | 16 | type LegacyAsyncComponent = ( 17 | resolve?: (res: LegacyAsyncReturnValue) => void, 18 | reject?: (reason?: any) => void 19 | ) => LegacyAsyncReturnValue | undefined 20 | 21 | const normalizedAsyncComponentMap = new Map() 22 | 23 | export function convertLegacyAsyncComponent(comp: LegacyAsyncComponent) { 24 | if (normalizedAsyncComponentMap.has(comp)) { 25 | return normalizedAsyncComponentMap.get(comp)! 26 | } 27 | 28 | // we have to call the function here due to how v2's API won't expose the 29 | // options until we call it 30 | let resolve: (res: LegacyAsyncReturnValue) => void 31 | let reject: (reason?: any) => void 32 | const fallbackPromise = new Promise((r, rj) => { 33 | ;(resolve = r), (reject = rj) 34 | }) 35 | 36 | const res = comp(resolve!, reject!) 37 | 38 | let converted: Component 39 | if (isPromise(res)) { 40 | converted = defineAsyncComponent(() => res) 41 | } else if (isObject(res) && !isVNode(res) && !isArray(res)) { 42 | converted = defineAsyncComponent({ 43 | loader: () => res.component, 44 | loadingComponent: res.loading, 45 | errorComponent: res.error, 46 | delay: res.delay, 47 | timeout: res.timeout 48 | }) 49 | } else if (res == null) { 50 | converted = defineAsyncComponent(() => fallbackPromise) 51 | } else { 52 | converted = comp as any // probably a v3 functional comp 53 | } 54 | normalizedAsyncComponentMap.set(comp, converted) 55 | return converted 56 | } 57 | --------------------------------------------------------------------------------