diff --git a/.eslintignore b/.eslintignore deleted file mode 100644 index 4d4f41e30..000000000 --- a/.eslintignore +++ /dev/null @@ -1,4 +0,0 @@ -node_modules -dist -temp -coverage diff --git a/.eslintrc.cjs b/.eslintrc.cjs deleted file mode 100644 index db6d35dde..000000000 --- a/.eslintrc.cjs +++ /dev/null @@ -1,134 +0,0 @@ -const { builtinModules } = require('node:module') -const DOMGlobals = ['window', 'document'] -const NodeGlobals = ['module', 'require'] - -const banConstEnum = { - selector: 'TSEnumDeclaration[const=true]', - message: - 'Please use non-const enums. This project automatically inlines enums.', -} - -/** - * @type {import('eslint-define-config').ESLintConfig} - */ -module.exports = { - parser: '@typescript-eslint/parser', - parserOptions: { - sourceType: 'module', - }, - plugins: ['jest', 'import', '@typescript-eslint'], - rules: { - 'no-debugger': 'error', - 'no-console': ['error', { allow: ['warn', 'error', 'info'] }], - // most of the codebase are expected to be env agnostic - 'no-restricted-globals': ['error', ...DOMGlobals, ...NodeGlobals], - - 'no-restricted-syntax': [ - 'error', - banConstEnum, - // since we target ES2015 for baseline support, we need to forbid object - // rest spread usage in destructure as it compiles into a verbose helper. - 'ObjectPattern > RestElement', - // tsc compiles assignment spread into Object.assign() calls, but esbuild - // still generates verbose helpers, so spread assignment is also prohiboted - 'ObjectExpression > SpreadElement', - 'AwaitExpression', - ], - 'sort-imports': ['error', { ignoreDeclarationSort: true }], - - 'import/no-nodejs-modules': [ - 'error', - { allow: builtinModules.map(mod => `node:${mod}`) }, - ], - // This rule enforces the preference for using '@ts-expect-error' comments in TypeScript - // code to indicate intentional type errors, improving code clarity and maintainability. - '@typescript-eslint/prefer-ts-expect-error': 'error', - // Enforce the use of 'import type' for importing types - '@typescript-eslint/consistent-type-imports': [ - 'error', - { - fixStyle: 'inline-type-imports', - disallowTypeAnnotations: false, - }, - ], - // Enforce the use of top-level import type qualifier when an import only has specifiers with inline type qualifiers - '@typescript-eslint/no-import-type-side-effects': 'error', - }, - overrides: [ - // tests, no restrictions (runs in Node / jest with jsdom) - { - files: ['**/__tests__/**', 'packages/dts-test/**'], - rules: { - 'no-console': 'off', - 'no-restricted-globals': 'off', - 'no-restricted-syntax': 'off', - 'jest/no-disabled-tests': 'error', - 'jest/no-focused-tests': 'error', - }, - }, - // shared, may be used in any env - { - files: ['packages/shared/**', '.eslintrc.cjs'], - rules: { - 'no-restricted-globals': 'off', - }, - }, - // Packages targeting DOM - { - files: ['packages/{vue,vue-compat,runtime-dom}/**'], - rules: { - 'no-restricted-globals': ['error', ...NodeGlobals], - }, - }, - // Packages targeting Node - { - files: ['packages/{compiler-sfc,compiler-ssr,server-renderer}/**'], - rules: { - 'no-restricted-globals': ['error', ...DOMGlobals], - 'no-restricted-syntax': ['error', banConstEnum], - }, - }, - // Private package, browser only + no syntax restrictions - { - files: [ - 'packages/template-explorer/**', - 'packages/sfc-playground/**', - 'playground/**', - ], - rules: { - 'no-restricted-globals': ['error', ...NodeGlobals], - 'no-restricted-syntax': ['error', banConstEnum], - 'no-console': 'off', - }, - }, - // JavaScript files - { - files: ['*.js', '*.cjs'], - rules: { - // We only do `no-unused-vars` checks for js files, TS files are checked by TypeScript itself. - 'no-unused-vars': ['error', { vars: 'all', args: 'none' }], - }, - }, - // Node scripts - { - files: [ - 'scripts/**', - './*.{js,ts}', - 'packages/*/*.js', - 'packages/vue/*/*.js', - ], - rules: { - 'no-restricted-globals': 'off', - 'no-restricted-syntax': ['error', banConstEnum], - 'no-console': 'off', - }, - }, - // Import nodejs modules in compiler-sfc - { - files: ['packages/compiler-sfc/src/**'], - rules: { - 'import/no-nodejs-modules': ['error', { allow: builtinModules }], - }, - }, - ], -} diff --git a/.github/renovate.json5 b/.github/renovate.json5 index fa130ca40..e87294b5b 100644 --- a/.github/renovate.json5 +++ b/.github/renovate.json5 @@ -34,7 +34,7 @@ { groupName: 'lint', matchPackageNames: ['simple-git-hooks', 'lint-staged'], - matchPackagePrefixes: ['@typescript-eslint', 'eslint', 'prettier'], + matchPackagePrefixes: ['typescript-eslint', 'eslint', 'prettier'], }, ], ignoreDeps: [ diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0e9ff69f9..a6c363b43 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -20,7 +20,7 @@ jobs: - uses: actions/checkout@v4 - name: Install pnpm - uses: pnpm/action-setup@v2 + uses: pnpm/action-setup@v3.0.0 - name: Install Node.js uses: actions/setup-node@v4 @@ -42,7 +42,7 @@ jobs: - uses: actions/checkout@v4 - name: Install pnpm - uses: pnpm/action-setup@v2 + uses: pnpm/action-setup@v3.0.0 - name: Install Node.js uses: actions/setup-node@v4 @@ -102,7 +102,7 @@ jobs: # - uses: actions/checkout@v4 # - name: Install pnpm - # uses: pnpm/action-setup@v2 + # uses: pnpm/action-setup@v3.0.0 # - name: Install Node.js # uses: actions/setup-node@v4 diff --git a/.github/workflows/size-data.yml b/.github/workflows/size-data.yml index a1c6a48c1..fa8a911e5 100644 --- a/.github/workflows/size-data.yml +++ b/.github/workflows/size-data.yml @@ -7,6 +7,7 @@ on: pull_request: branches: - main + - minor permissions: contents: read @@ -22,7 +23,7 @@ jobs: - uses: actions/checkout@v4 - name: Install pnpm - uses: pnpm/action-setup@v2 + uses: pnpm/action-setup@v3.0.0 - name: Install Node.js uses: actions/setup-node@v4 diff --git a/.github/workflows/size-report.yml b/.github/workflows/size-report.yml index b6f1f4728..590237206 100644 --- a/.github/workflows/size-report.yml +++ b/.github/workflows/size-report.yml @@ -24,7 +24,7 @@ jobs: - uses: actions/checkout@v4 - name: Install pnpm - uses: pnpm/action-setup@v2 + uses: pnpm/action-setup@v3.0.0 - name: Install Node.js uses: actions/setup-node@v4 diff --git a/.gitignore b/.gitignore index 810f88526..9dd21f59b 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,4 @@ TODOs.md .eslintcache dts-build/packages *.tsbuildinfo +*.tgz diff --git a/.vscode/launch.json b/.vscode/launch.json index b616400b4..9fc03aa9b 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -5,24 +5,15 @@ "version": "0.2.0", "configurations": [ { - "name": "Jest", "type": "node", "request": "launch", - "program": "${workspaceFolder}/node_modules/.bin/jest", - "stopOnEntry": false, - "args": ["${fileBasename}", "--runInBand", "--detectOpenHandles"], - "cwd": "${workspaceFolder}", - "preLaunchTask": null, - "runtimeExecutable": null, - "runtimeArgs": ["--nolazy"], - "env": { - "NODE_ENV": "development" - }, - "console": "integratedTerminal", - "sourceMaps": true, - "windows": { - "program": "${workspaceFolder}/node_modules/jest/bin/jest" - } + "name": "Vitest - Debug Current Test File", + "autoAttachChildProcesses": true, + "skipFiles": ["/**", "**/node_modules/**"], + "program": "${workspaceRoot}/node_modules/vitest/vitest.mjs", + "args": ["run", "${relativeFile}"], + "smartStep": true, + "console": "integratedTerminal" } ] } diff --git a/CHANGELOG.md b/CHANGELOG.md index 28447dfe3..fe351268a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,83 @@ +## [3.4.25](https://github.com/vuejs/core/compare/v3.4.24...v3.4.25) (2024-04-24) + + +### Bug Fixes + +* **defineModel:** align prod mode runtime type generation with defineProps ([4253a57](https://github.com/vuejs/core/commit/4253a57f1703a7f1ac701d77e0a235689203461d)), closes [#10769](https://github.com/vuejs/core/issues/10769) +* **runtime-core:** properly get keepAlive child ([#10772](https://github.com/vuejs/core/issues/10772)) ([3724693](https://github.com/vuejs/core/commit/3724693a25c3f2dd13d70a8a1af760b03a4fb783)), closes [#10771](https://github.com/vuejs/core/issues/10771) +* **runtime-core:** use normal object as internal prototype for attrs and slots ([064e82f](https://github.com/vuejs/core/commit/064e82f5855f30fe0b77fe9b5e4dd22700fd634d)), closes [/github.com/vuejs/core/commit/6df53d85a207986128159d88565e6e7045db2add#r141304923](https://github.com//github.com/vuejs/core/commit/6df53d85a207986128159d88565e6e7045db2add/issues/r141304923) + + + +## [3.4.24](https://github.com/vuejs/core/compare/v3.4.23...v3.4.24) (2024-04-22) + + +### Bug Fixes + +* **compiler-core:** handle template ref bound via v-bind object on v-for ([#10706](https://github.com/vuejs/core/issues/10706)) ([da7adef](https://github.com/vuejs/core/commit/da7adefa844265eecc9c336abfc727bc05b4f16e)), closes [#10696](https://github.com/vuejs/core/issues/10696) +* **compiler-core:** properly parse await expressions in edge cases ([b92c25f](https://github.com/vuejs/core/commit/b92c25f53dff0fc1687f57ca4033d0ac25218940)), closes [#10754](https://github.com/vuejs/core/issues/10754) +* **compiler-sfc:** handle readonly operator and ReadonlyArray/Map/Set types ([5cef52a](https://github.com/vuejs/core/commit/5cef52a5c23ba8ba3239e6def03b8ff008d3cc72)), closes [#10726](https://github.com/vuejs/core/issues/10726) +* **compiler-ssr:** fix hydration mismatch for conditional slot in transition ([f12c81e](https://github.com/vuejs/core/commit/f12c81efca3fcf9a7ce478af2261ad6ab9b0bfd7)), closes [#10743](https://github.com/vuejs/core/issues/10743) +* **compiler-ssr:** fix v-html SSR for nullish values ([1ff4076](https://github.com/vuejs/core/commit/1ff407676f9495883b459779a9b0370d7588b51f)), closes [#10725](https://github.com/vuejs/core/issues/10725) +* **deps:** update compiler ([#10760](https://github.com/vuejs/core/issues/10760)) ([15df5c1](https://github.com/vuejs/core/commit/15df5c1b261b9b471eb811fd47ab7b3cfc41cf83)) +* **runtime-core:** fix edge case of KeepAlive inside Transition with slot children ([#10719](https://github.com/vuejs/core/issues/10719)) ([e51ca61](https://github.com/vuejs/core/commit/e51ca61ca060b2772e967d169548fc2f58fce6d1)), closes [#10708](https://github.com/vuejs/core/issues/10708) +* **runtime-core:** further fix slots _ctx check ([cde7f05](https://github.com/vuejs/core/commit/cde7f05787d16dbb93d9419ef5331adf992816fd)), closes [#10724](https://github.com/vuejs/core/issues/10724) +* **runtime-core:** props should be readonly via direct template access ([b93f264](https://github.com/vuejs/core/commit/b93f26464785de227b88c51a88328ae80e80d804)), closes [#8216](https://github.com/vuejs/core/issues/8216) [#10736](https://github.com/vuejs/core/issues/10736) +* **transition:** transition is breaking/flickering when enter is canceled ([#10688](https://github.com/vuejs/core/issues/10688)) ([65109a7](https://github.com/vuejs/core/commit/65109a70f187473edae8cf4df11af3c33345e6f6)) + + + +## [3.4.23](https://github.com/vuejs/core/compare/v3.4.22...v3.4.23) (2024-04-16) + + +### Bug Fixes + +* **runtime-core:** fix regression for $attrs tracking in slots ([6930e60](https://github.com/vuejs/core/commit/6930e60787e4905a50417190263ae7dd46cf5409)), closes [#10710](https://github.com/vuejs/core/issues/10710) +* **runtime-core:** use same internal object mechanism for slots ([6df53d8](https://github.com/vuejs/core/commit/6df53d85a207986128159d88565e6e7045db2add)), closes [#10709](https://github.com/vuejs/core/issues/10709) + + + +## [3.4.22](https://github.com/vuejs/core/compare/v3.4.21...v3.4.22) (2024-04-15) + + +### Bug Fixes + +* **compat:** fix $options mutation + adjust private API initialization ([d58d133](https://github.com/vuejs/core/commit/d58d133b1cde5085cc5ab0012d544cafd62a6ee6)), closes [#10626](https://github.com/vuejs/core/issues/10626) [#10636](https://github.com/vuejs/core/issues/10636) +* **compile-sfc:** analyze v-bind shorthand usage in template ([#10518](https://github.com/vuejs/core/issues/10518)) ([e5919d4](https://github.com/vuejs/core/commit/e5919d4658cfe0bb18c76611dd3c3432c57f94ab)), closes [#10515](https://github.com/vuejs/core/issues/10515) +* **compiler-core:** fix loc.source for end tags with whitespace before > ([16174da](https://github.com/vuejs/core/commit/16174da21d6c8ac0aae027dd964fc35e221ded0a)), closes [#10694](https://github.com/vuejs/core/issues/10694) [#10695](https://github.com/vuejs/core/issues/10695) +* **compiler-core:** fix v-bind shorthand for component :is ([04af950](https://github.com/vuejs/core/commit/04af9504a720c8e6de26c04b1282cf14fa1bcee3)), closes [#10469](https://github.com/vuejs/core/issues/10469) [#10471](https://github.com/vuejs/core/issues/10471) +* **compiler-sfc:** :is() and :where() in compound selectors ([#10522](https://github.com/vuejs/core/issues/10522)) ([660cadc](https://github.com/vuejs/core/commit/660cadc7aadb909ef33a6055c4374902a82607a4)), closes [#10511](https://github.com/vuejs/core/issues/10511) +* **compiler-sfc:** also search for `.tsx` when type import's extension is omitted ([#10637](https://github.com/vuejs/core/issues/10637)) ([34106bc](https://github.com/vuejs/core/commit/34106bc9c715247211273bb9c64712f04bd4879d)), closes [#10635](https://github.com/vuejs/core/issues/10635) +* **compiler-sfc:** fix defineModel coercion for boolean + string union types ([#9603](https://github.com/vuejs/core/issues/9603)) ([0cef65c](https://github.com/vuejs/core/commit/0cef65cee411356e721bbc90d731fc52fc8fce94)), closes [#9587](https://github.com/vuejs/core/issues/9587) [#10676](https://github.com/vuejs/core/issues/10676) +* **compiler-sfc:** fix universal selector scope ([#10551](https://github.com/vuejs/core/issues/10551)) ([54a6afa](https://github.com/vuejs/core/commit/54a6afa75a546078e901ce0882da53b97420fe94)), closes [#10548](https://github.com/vuejs/core/issues/10548) +* **compiler-sfc:** use options module name if options provide runtimeModuleName options ([#10457](https://github.com/vuejs/core/issues/10457)) ([e76d743](https://github.com/vuejs/core/commit/e76d7430aa7470342f3fe263145a0fa92f5898ca)), closes [#10454](https://github.com/vuejs/core/issues/10454) +* **custom-element:** avoid setting attr to null if it is removed ([#9012](https://github.com/vuejs/core/issues/9012)) ([b49306a](https://github.com/vuejs/core/commit/b49306adff4572d90a42ccd231387f16eb966bbe)), closes [#9006](https://github.com/vuejs/core/issues/9006) [#10324](https://github.com/vuejs/core/issues/10324) +* **hydration:** properly handle optimized mode during hydrate node ([#10638](https://github.com/vuejs/core/issues/10638)) ([2ec06fd](https://github.com/vuejs/core/commit/2ec06fd6c8383e11cdf4efcab1707f973bd6a54c)), closes [#10607](https://github.com/vuejs/core/issues/10607) +* **reactivity:** computed should not be detected as true by isProxy ([#10401](https://github.com/vuejs/core/issues/10401)) ([9da34d7](https://github.com/vuejs/core/commit/9da34d7af81607fddd1f32f21b3b4002402ff1cc)) +* **reactivity:** fix hasOwnProperty key coercion edge cases ([969c5fb](https://github.com/vuejs/core/commit/969c5fb30f4c725757c7385abfc74772514eae4b)) +* **reactivity:** fix tracking when hasOwnProperty is called with non-string value ([c3c5dc9](https://github.com/vuejs/core/commit/c3c5dc93fbccc196771458f0b43cd5b7ad1863f4)), closes [#10455](https://github.com/vuejs/core/issues/10455) [#10464](https://github.com/vuejs/core/issues/10464) +* **runtime-core:** fix errorHandler causes an infinite loop during execution ([#9575](https://github.com/vuejs/core/issues/9575)) ([ab59bed](https://github.com/vuejs/core/commit/ab59bedae4e5e40b28804d88a51305b236d4a873)) +* **runtime-core:** handle invalid values in callWithAsyncErrorHandling ([53d15d3](https://github.com/vuejs/core/commit/53d15d3f76184eed67a18d35e43d9a2062f8e121)) +* **runtime-core:** show hydration mismatch details for non-rectified mismatches too when __PROD_HYDRATION_MISMATCH_DETAILS__ is set ([#10599](https://github.com/vuejs/core/issues/10599)) ([0dea7f9](https://github.com/vuejs/core/commit/0dea7f9a260d93eb6c39aabac8c94c2c9b2042dd)) +* **runtime-dom:** `v-model` string/number coercion for multiselect options ([#10576](https://github.com/vuejs/core/issues/10576)) ([db374e5](https://github.com/vuejs/core/commit/db374e54c9f5e07324728b85c74eca84e28dd352)) +* **runtime-dom:** fix css v-bind for suspensed components ([#8523](https://github.com/vuejs/core/issues/8523)) ([67722ba](https://github.com/vuejs/core/commit/67722ba23b7c36ab8f3fa2d2b4df08e4ddc322e1)), closes [#8520](https://github.com/vuejs/core/issues/8520) +* **runtime-dom:** force update v-model number with leading 0 ([#10506](https://github.com/vuejs/core/issues/10506)) ([15ffe8f](https://github.com/vuejs/core/commit/15ffe8f2c954359770c57e4d9e589b0b622e4a60)), closes [#10503](https://github.com/vuejs/core/issues/10503) [#10615](https://github.com/vuejs/core/issues/10615) +* **runtime-dom:** sanitize wrongly passed string value as event handler ([#8953](https://github.com/vuejs/core/issues/8953)) ([7ccd453](https://github.com/vuejs/core/commit/7ccd453dd004076cad49ec9f56cd5fe97b7b6ed8)), closes [#8818](https://github.com/vuejs/core/issues/8818) +* **ssr:** don't render v-if comments in TransitionGroup ([#6732](https://github.com/vuejs/core/issues/6732)) ([5a96267](https://github.com/vuejs/core/commit/5a9626708e970c6fc0b6f786e3c80c22273d126f)), closes [#6715](https://github.com/vuejs/core/issues/6715) +* **Transition:** ensure the KeepAlive children unmount w/ out-in mode ([#10632](https://github.com/vuejs/core/issues/10632)) ([fc99e4d](https://github.com/vuejs/core/commit/fc99e4d3f01b190ef9fd3c218a668ba9124a32bc)), closes [#10620](https://github.com/vuejs/core/issues/10620) +* **TransitionGroup:** avoid set transition hooks for comment nodes and text nodes ([#9421](https://github.com/vuejs/core/issues/9421)) ([140a768](https://github.com/vuejs/core/commit/140a7681cc3bba22f55d97fd85a5eafe97a1230f)), closes [#4621](https://github.com/vuejs/core/issues/4621) [#4622](https://github.com/vuejs/core/issues/4622) [#5153](https://github.com/vuejs/core/issues/5153) [#5168](https://github.com/vuejs/core/issues/5168) [#7898](https://github.com/vuejs/core/issues/7898) [#9067](https://github.com/vuejs/core/issues/9067) +* **types:** avoid merging object union types when using withDefaults ([#10596](https://github.com/vuejs/core/issues/10596)) ([37ba93c](https://github.com/vuejs/core/commit/37ba93c213a81f99a68a99ef5d4065d61b150ba3)), closes [#10594](https://github.com/vuejs/core/issues/10594) + + +### Performance Improvements + +* add `__NO_SIDE_EFFECTS__` comments ([#9053](https://github.com/vuejs/core/issues/9053)) ([d46df6b](https://github.com/vuejs/core/commit/d46df6bdb14b0509eb2134b3f85297a306821c61)) +* optimize component props/slots internal object checks ([6af733d](https://github.com/vuejs/core/commit/6af733d68eb400a3d2c5ef5f465fff32b72a324e)) +* **ssr:** avoid calling markRaw on component instance proxy ([4bc9f39](https://github.com/vuejs/core/commit/4bc9f39f028af7313e5cf24c16915a1985d27bf8)) +* **ssr:** optimize setup context creation for ssr in v8 ([ca84316](https://github.com/vuejs/core/commit/ca84316bfb3410efe21333670a6ad5cd21857396)) + + + ## [3.4.21](https://github.com/vuejs/core/compare/v3.4.20...v3.4.21) (2024-02-28) diff --git a/FUNDING.json b/FUNDING.json new file mode 100644 index 000000000..552dc453c --- /dev/null +++ b/FUNDING.json @@ -0,0 +1,7 @@ +{ + "drips": { + "ethereum": { + "ownedBy": "0x5393BdeA2a020769256d9f337B0fc81a2F64850A" + } + } +} diff --git a/README.md b/README.md index 25a5ada78..8f278dab9 100644 --- a/README.md +++ b/README.md @@ -5,15 +5,14 @@ This repository is a fork of [vuejs/core](https://github.com/vuejs/core) and is - [Vapor Playground](https://vapor-repl.netlify.app/) - [Vapor Template Explorer](https://vapor-template-explorer.netlify.app/) -## TODO - -PR are welcome! +## Vue Vapor Team -- [Issues with `todo` tag](https://github.com/vuejs/core-vapor/labels/todo) -- To-do list below (**create an issue** for discussion before a big feat/bug PR is required) -- `// TODO` comments in code (`compiler-vapor` and `runtime-vapor` packages) +- [Evan You](https://github.com/yyx990803) - Creator and designer of Vue.js and Vue Vapor. +- [Kevin Deng 三咲智子](https://github.com/sxzz) - Lead contributor and developer for Vue Vapor. +- [Rizumu Ayaka](https://github.com/LittleSound) - Key contributor to compiler and reactivity. +- [Ubugeeei](https://github.com/Ubugeeei) - Key contributor to components. ---- +## TODO - [x] Counter App - [x] simple bindings @@ -45,8 +44,7 @@ PR are welcome! - [x] `v-model` - [x] `v-if` / `v-else` / `v-else-if` - [ ] `v-for` - - [x] basic - - [ ] object + - [ ] destructure - [x] `v-show` - [x] Fragment - [ ] Codegen @@ -54,33 +52,19 @@ PR are welcome! - [x] indent - [x] Source map - [x] scope id - - [ ] Function mode + - [ ] Function mode [on hold] - [ ] SSR +- [ ] [Component](https://github.com/vuejs/core-vapor/issues/4) + - WIP - [ ] Built-in Components - [ ] Transition - [ ] TransitionGroup - [ ] KeepAlive - [ ] Teleport - [ ] Suspense -- [ ] [Component](https://github.com/vuejs/core-vapor/issues/4) - [ ] Performance & Optimization - [ ] remove unnecessary close tag `` -## Codes Copied From `runtime-core` - -The code provided here is a duplicate from `runtime-core` as Vapor cannot import it directly. For the time being, we copy any necessary logic from runtime-core into runtime-vapor and document it accordingly. Once the experimental development phase concludes, we will consolidate these elements into a separate software package. - -- packages/runtime-vapor/src/apiWatch.ts -- packages/runtime-vapor/src/component.ts -- packages/runtime-vapor/src/componentEmits.ts -- packages/runtime-vapor/src/componentProps.ts -- packages/runtime-vapor/src/enums.ts -- packages/runtime-vapor/src/errorHandling.ts -- packages/runtime-vapor/src/scheduler.ts -- packages/runtime-vapor/src/warning.ts - -If there are more, please feel free to add. - ## Sponsors Vue.js is an MIT-licensed open source project with its ongoing development made possible entirely by the support of these awesome [backers](https://github.com/vuejs/core/blob/main/BACKERS.md). If you'd like to join them, please consider [ sponsoring Vue's development](https://vuejs.org/sponsor/). diff --git a/SECURITY.md b/SECURITY.md index dac6018b5..743dfa17d 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -5,3 +5,10 @@ To report a vulnerability, please email security@vuejs.org. 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. 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. + +## Security Hall of Fame + +We would like to thank the following security researchers for responsibly disclosing security issues to us. + +- Jeet Pal - [@jeetpal2007](https://github.com/jeetpal2007) | [Email](jeetpal2007@gmail.com) | [LinkedIn](https://in.linkedin.com/in/jeet-pal-22601a290 ) +- Mix - [@mnixry](https://github.com/mnixry) diff --git a/eslint.config.js b/eslint.config.js new file mode 100644 index 000000000..b1a46ff0e --- /dev/null +++ b/eslint.config.js @@ -0,0 +1,172 @@ +import importX from 'eslint-plugin-import-x' +import tseslint from 'typescript-eslint' +import vitest from 'eslint-plugin-vitest' +import { builtinModules } from 'node:module' + +const DOMGlobals = ['window', 'document'] +const NodeGlobals = ['module', 'require'] + +const banConstEnum = { + selector: 'TSEnumDeclaration[const=true]', + message: + 'Please use non-const enums. This project automatically inlines enums.', +} + +export default tseslint.config( + { + files: ['**/*.js', '**/*.ts', '**/*.tsx'], + extends: [tseslint.configs.base], + plugins: { + 'import-x': importX, + }, + rules: { + 'no-debugger': 'error', + 'no-console': ['error', { allow: ['warn', 'error', 'info'] }], + // most of the codebase are expected to be env agnostic + 'no-restricted-globals': ['error', ...DOMGlobals, ...NodeGlobals], + + 'no-restricted-syntax': [ + 'error', + banConstEnum, + { + selector: 'ObjectPattern > RestElement', + message: + 'Our output target is ES2016, and object rest spread results in ' + + 'verbose helpers and should be avoided.', + }, + { + selector: 'ObjectExpression > SpreadElement', + message: + 'esbuild transpiles object spread into very verbose inline helpers.\n' + + 'Please use the `extend` helper from @vue/shared instead.', + }, + { + selector: 'AwaitExpression', + message: + 'Our output target is ES2016, so async/await syntax should be avoided.', + }, + ], + 'sort-imports': ['error', { ignoreDeclarationSort: true }], + + 'import-x/no-nodejs-modules': [ + 'error', + { allow: builtinModules.map(mod => `node:${mod}`) }, + ], + // This rule enforces the preference for using '@ts-expect-error' comments in TypeScript + // code to indicate intentional type errors, improving code clarity and maintainability. + '@typescript-eslint/prefer-ts-expect-error': 'error', + // Enforce the use of 'import type' for importing types + '@typescript-eslint/consistent-type-imports': [ + 'error', + { + fixStyle: 'inline-type-imports', + disallowTypeAnnotations: false, + }, + ], + // Enforce the use of top-level import type qualifier when an import only has specifiers with inline type qualifiers + '@typescript-eslint/no-import-type-side-effects': 'error', + }, + }, + + // tests, no restrictions (runs in Node / Vitest with jsdom) + { + files: ['**/__tests__/**', 'packages/dts-test/**'], + plugins: { vitest }, + languageOptions: { + globals: { + ...vitest.environments.env.globals, + }, + }, + rules: { + 'no-console': 'off', + 'no-restricted-globals': 'off', + 'no-restricted-syntax': 'off', + 'vitest/no-disabled-tests': 'error', + 'vitest/no-focused-tests': 'error', + }, + }, + + // shared, may be used in any env + { + files: ['packages/shared/**', 'eslint.config.js'], + rules: { + 'no-restricted-globals': 'off', + }, + }, + + // Packages targeting DOM + { + files: ['packages/{vue,vue-compat,runtime-dom}/**'], + rules: { + 'no-restricted-globals': ['error', ...NodeGlobals], + }, + }, + + // Packages targeting Node + { + files: ['packages/{compiler-sfc,compiler-ssr,server-renderer}/**'], + rules: { + 'no-restricted-globals': ['error', ...DOMGlobals], + 'no-restricted-syntax': ['error', banConstEnum], + }, + }, + + // Private package, browser only + no syntax restrictions + { + files: [ + 'packages/template-explorer/**', + 'packages/sfc-playground/**', + 'playground/**', + ], + rules: { + 'no-restricted-globals': ['error', ...NodeGlobals], + 'no-restricted-syntax': ['error', banConstEnum], + 'no-console': 'off', + }, + }, + + // JavaScript files + { + files: ['*.js'], + rules: { + // We only do `no-unused-vars` checks for js files, TS files are checked by TypeScript itself. + 'no-unused-vars': ['error', { vars: 'all', args: 'none' }], + }, + }, + + // Node scripts + { + files: [ + 'eslint.config.js', + 'rollup.config.js', + 'scripts/**', + './*.{js,ts}', + 'packages/*/*.js', + 'packages/vue/*/*.js', + ], + rules: { + 'no-restricted-globals': 'off', + 'no-restricted-syntax': ['error', banConstEnum], + 'no-console': 'off', + }, + }, + + // Import nodejs modules in compiler-sfc + { + files: ['packages/compiler-sfc/src/**'], + rules: { + 'import-x/no-nodejs-modules': ['error', { allow: builtinModules }], + }, + }, + + { + ignores: [ + '**/dist/', + '**/temp/', + '**/coverage/', + '.idea/', + 'explorations/', + 'dts-build/packages', + ], + }, +) diff --git a/package.json b/package.json index 030233a61..000bb6e80 100644 --- a/package.json +++ b/package.json @@ -1,26 +1,26 @@ { "private": true, "version": "3.0.0-vapor", - "packageManager": "pnpm@8.15.4", + "packageManager": "pnpm@9.0.5", "type": "module", "scripts": { "dev": "node scripts/dev.js vue vue-vapor", "build": "node scripts/build.js", - "build-dts": "tsc -p tsconfig.build.json && rollup -c rollup.dts.config.js", + "build-dts": "tsc -p tsconfig.build-browser.json && tsc -p tsconfig.build-node.json && rollup -c rollup.dts.config.js", "clean": "rimraf packages/*/dist temp .eslintcache", "size": "run-s \"size-*\" && tsx scripts/usage-size.ts", "size-global": "node scripts/build.js vue vue-vapor runtime-dom runtime-vapor compiler-dom compiler-vapor -f global -p --size", "size-esm-runtime": "node scripts/build.js vue vue-vapor -f esm-bundler-runtime", "size-esm": "node scripts/build.js runtime-dom runtime-vapor runtime-core reactivity shared -f esm-bundler", "check": "tsc --incremental --noEmit", - "lint": "eslint --cache --ext .js,.ts,.tsx . --ignore-path .gitignore", + "lint": "eslint --cache .", "format": "prettier --write --cache .", "format-check": "prettier --check --cache .", "test": "vitest", "test-unit": "vitest -c vitest.unit.config.ts", "test-e2e": "node scripts/build.js vue vue-vapor -f global -d && vitest -c vitest.e2e.config.ts", "test-dts": "run-s build-dts test-dts-only", - "test-dts-only": "tsc -p ./packages/dts-test/tsconfig.test.json", + "test-dts-only": "tsc -p packages/dts-built-test/tsconfig.json && tsc -p ./packages/dts-test/tsconfig.test.json", "test-coverage": "vitest -c vitest.unit.config.ts --coverage", "test-bench": "vitest bench", "release": "node scripts/release.js", @@ -59,9 +59,9 @@ "node": ">=18.12.0" }, "devDependencies": { - "@babel/parser": "^7.23.9", - "@babel/types": "^7.23.9", - "@codspeed/vitest-plugin": "^2.3.1", + "@babel/parser": "^7.24.4", + "@babel/types": "^7.24.0", + "@codspeed/vitest-plugin": "^3.1.0", "@rollup/plugin-alias": "^5.1.0", "@rollup/plugin-commonjs": "^25.0.7", "@rollup/plugin-json": "^6.1.0", @@ -70,50 +70,59 @@ "@rollup/plugin-terser": "^0.4.4", "@types/hash-sum": "^1.0.2", "@types/minimist": "^1.2.5", - "@types/node": "^20.11.20", + "@types/node": "^20.12.7", "@types/semver": "^7.5.8", - "@typescript-eslint/eslint-plugin": "^7.0.2", - "@typescript-eslint/parser": "^7.0.2", - "@vitest/coverage-istanbul": "^1.3.1", - "@vitest/ui": "^1.2.2", + "@vitest/coverage-istanbul": "^1.5.0", + "@vitest/ui": "^1.5.0", "@vue/consolidate": "1.0.0", "conventional-changelog-cli": "^4.1.0", "enquirer": "^2.4.1", - "esbuild": "^0.20.1", + "esbuild": "^0.20.2", "esbuild-plugin-polyfill-node": "^0.3.0", - "eslint": "^8.57.0", - "eslint-define-config": "^2.1.0", - "eslint-plugin-import": "npm:eslint-plugin-i@^2.29.1", - "eslint-plugin-jest": "^27.9.0", + "eslint": "^9.0.0", + "eslint-plugin-import-x": "^0.5.0", + "eslint-plugin-vitest": "^0.5.3", "estree-walker": "^2.0.2", "execa": "^8.0.1", "jsdom": "^24.0.0", "lint-staged": "^15.2.2", "lodash": "^4.17.21", - "magic-string": "^0.30.7", + "magic-string": "^0.30.10", "markdown-table": "^3.0.3", - "marked": "^12.0.0", + "marked": "^12.0.2", "minimist": "^1.2.8", "npm-run-all2": "^6.1.2", "picocolors": "^1.0.0", "prettier": "^3.2.5", "pretty-bytes": "^6.1.1", "pug": "^3.0.2", - "puppeteer": "~22.2.0", + "puppeteer": "~22.6.5", "rimraf": "^5.0.5", - "rollup": "^4.12.0", + "rollup": "^4.16.1", "rollup-plugin-dts": "^6.1.0", "rollup-plugin-esbuild": "^6.1.1", "rollup-plugin-polyfill-node": "^0.13.0", "semver": "^7.6.0", "serve": "^14.2.1", - "simple-git-hooks": "^2.9.0", - "terser": "^5.28.1", + "simple-git-hooks": "^2.11.1", + "terser": "^5.30.3", "todomvc-app-css": "^2.4.3", "tslib": "^2.6.2", - "tsx": "^4.7.1", - "typescript": "^5.2.2", - "vite": "^5.1.4", - "vitest": "^1.3.1" + "tsx": "^4.7.2", + "typescript": "~5.4.5", + "typescript-eslint": "^7.6.0", + "vite": "^5.2.10", + "vitest": "^1.5.0" + }, + "pnpm": { + "peerDependencyRules": { + "allowedVersions": { + "typescript-eslint>eslint": "^9.0.0", + "@typescript-eslint/eslint-plugin>eslint": "^9.0.0", + "@typescript-eslint/parser>eslint": "^9.0.0", + "@typescript-eslint/type-utils>eslint": "^9.0.0", + "@typescript-eslint/utils>eslint": "^9.0.0" + } + } } } diff --git a/packages/compiler-core/__tests__/parse.spec.ts b/packages/compiler-core/__tests__/parse.spec.ts index d5bdb4bc5..22fb209cf 100644 --- a/packages/compiler-core/__tests__/parse.spec.ts +++ b/packages/compiler-core/__tests__/parse.spec.ts @@ -16,8 +16,6 @@ import { import { baseParse } from '../src/parser' import type { Program } from '@babel/types' -/* eslint jest/no-disabled-tests: "off" */ - describe('compiler: parse', () => { describe('Text', () => { test('simple text', () => { @@ -2070,6 +2068,16 @@ describe('compiler: parse', () => { baseParse(``, { parseMode: 'sfc', onError() {} }) expect(() => baseParse(`{ foo }`)).not.toThrow() }) + + test('correct loc when the closing > is foarmatted', () => { + const [span] = baseParse(``).children + + expect(span.loc.source).toBe('') + expect(span.loc.start.offset).toBe(0) + expect(span.loc.end.offset).toBe(27) + }) }) describe('decodeEntities option', () => { @@ -2166,7 +2174,7 @@ describe('compiler: parse', () => { }) test('should remove leading newline character immediately following the pre element start tag', () => { - const ast = baseParse(`
\n  foo  bar  
`, { + const ast = parse(`
\n  foo  bar  
`, { isPreTag: tag => tag === 'pre', }) expect(ast.children).toHaveLength(1) @@ -2176,7 +2184,7 @@ describe('compiler: parse', () => { }) test('should NOT remove leading newline character immediately following child-tag of pre element', () => { - const ast = baseParse(`
\n  foo  bar  
`, { + const ast = parse(`
\n  foo  bar  
`, { isPreTag: tag => tag === 'pre', }) const preElement = ast.children[0] as ElementNode @@ -2187,7 +2195,7 @@ describe('compiler: parse', () => { }) test('self-closing pre tag', () => { - const ast = baseParse(`
\n  foo   bar`, {
+      const ast = parse(`
\n  foo   bar`, {
         isPreTag: tag => tag === 'pre',
       })
       const elementAfterPre = ast.children[1] as ElementNode
@@ -2196,7 +2204,7 @@ describe('compiler: parse', () => {
     })
 
     test('should NOT condense whitespaces in RCDATA text mode', () => {
-      const ast = baseParse(``, {
+      const ast = parse(``, {
         parseMode: 'html',
       })
       const preElement = ast.children[0] as ElementNode
diff --git a/packages/compiler-core/__tests__/transforms/__snapshots__/transformElement.spec.ts.snap b/packages/compiler-core/__tests__/transforms/__snapshots__/transformElement.spec.ts.snap
new file mode 100644
index 000000000..3da778eb6
--- /dev/null
+++ b/packages/compiler-core/__tests__/transforms/__snapshots__/transformElement.spec.ts.snap
@@ -0,0 +1,228 @@
+// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
+
+exports[`compiler: v-for > codegen > basic v-for 1`] = `
+"const _Vue = Vue
+
+return function render(_ctx, _cache) {
+  with (_ctx) {
+    const { renderList: _renderList, Fragment: _Fragment, openBlock: _openBlock, createElementBlock: _createElementBlock } = _Vue
+
+    return (_openBlock(true), _createElementBlock(_Fragment, null, _renderList(items, (item) => {
+      return (_openBlock(), _createElementBlock("span"))
+    }), 256 /* UNKEYED_FRAGMENT */))
+  }
+}"
+`;
+
+exports[`compiler: v-for > codegen > keyed template v-for 1`] = `
+"const _Vue = Vue
+
+return function render(_ctx, _cache) {
+  with (_ctx) {
+    const { renderList: _renderList, Fragment: _Fragment, openBlock: _openBlock, createElementBlock: _createElementBlock, createElementVNode: _createElementVNode } = _Vue
+
+    return (_openBlock(true), _createElementBlock(_Fragment, null, _renderList(items, (item) => {
+      return (_openBlock(), _createElementBlock(_Fragment, { key: item }, [
+        "hello",
+        _createElementVNode("span")
+      ], 64 /* STABLE_FRAGMENT */))
+    }), 128 /* KEYED_FRAGMENT */))
+  }
+}"
+`;
+
+exports[`compiler: v-for > codegen > keyed v-for 1`] = `
+"const _Vue = Vue
+
+return function render(_ctx, _cache) {
+  with (_ctx) {
+    const { renderList: _renderList, Fragment: _Fragment, openBlock: _openBlock, createElementBlock: _createElementBlock } = _Vue
+
+    return (_openBlock(true), _createElementBlock(_Fragment, null, _renderList(items, (item) => {
+      return (_openBlock(), _createElementBlock("span", { key: item }))
+    }), 128 /* KEYED_FRAGMENT */))
+  }
+}"
+`;
+
+exports[`compiler: v-for > codegen > skipped key 1`] = `
+"const _Vue = Vue
+
+return function render(_ctx, _cache) {
+  with (_ctx) {
+    const { renderList: _renderList, Fragment: _Fragment, openBlock: _openBlock, createElementBlock: _createElementBlock } = _Vue
+
+    return (_openBlock(true), _createElementBlock(_Fragment, null, _renderList(items, (item, __, index) => {
+      return (_openBlock(), _createElementBlock("span"))
+    }), 256 /* UNKEYED_FRAGMENT */))
+  }
+}"
+`;
+
+exports[`compiler: v-for > codegen > skipped value & key 1`] = `
+"const _Vue = Vue
+
+return function render(_ctx, _cache) {
+  with (_ctx) {
+    const { renderList: _renderList, Fragment: _Fragment, openBlock: _openBlock, createElementBlock: _createElementBlock } = _Vue
+
+    return (_openBlock(true), _createElementBlock(_Fragment, null, _renderList(items, (_, __, index) => {
+      return (_openBlock(), _createElementBlock("span"))
+    }), 256 /* UNKEYED_FRAGMENT */))
+  }
+}"
+`;
+
+exports[`compiler: v-for > codegen > skipped value 1`] = `
+"const _Vue = Vue
+
+return function render(_ctx, _cache) {
+  with (_ctx) {
+    const { renderList: _renderList, Fragment: _Fragment, openBlock: _openBlock, createElementBlock: _createElementBlock } = _Vue
+
+    return (_openBlock(true), _createElementBlock(_Fragment, null, _renderList(items, (_, key, index) => {
+      return (_openBlock(), _createElementBlock("span"))
+    }), 256 /* UNKEYED_FRAGMENT */))
+  }
+}"
+`;
+
+exports[`compiler: v-for > codegen > template v-for 1`] = `
+"const _Vue = Vue
+
+return function render(_ctx, _cache) {
+  with (_ctx) {
+    const { renderList: _renderList, Fragment: _Fragment, openBlock: _openBlock, createElementBlock: _createElementBlock, createElementVNode: _createElementVNode } = _Vue
+
+    return (_openBlock(true), _createElementBlock(_Fragment, null, _renderList(items, (item) => {
+      return (_openBlock(), _createElementBlock(_Fragment, null, [
+        "hello",
+        _createElementVNode("span")
+      ], 64 /* STABLE_FRAGMENT */))
+    }), 256 /* UNKEYED_FRAGMENT */))
+  }
+}"
+`;
+
+exports[`compiler: v-for > codegen > template v-for key injection with single child 1`] = `
+"const _Vue = Vue
+
+return function render(_ctx, _cache) {
+  with (_ctx) {
+    const { renderList: _renderList, Fragment: _Fragment, openBlock: _openBlock, createElementBlock: _createElementBlock } = _Vue
+
+    return (_openBlock(true), _createElementBlock(_Fragment, null, _renderList(items, (item) => {
+      return (_openBlock(), _createElementBlock("span", {
+        key: item.id,
+        id: item.id
+      }, null, 8 /* PROPS */, ["id"]))
+    }), 128 /* KEYED_FRAGMENT */))
+  }
+}"
+`;
+
+exports[`compiler: v-for > codegen > template v-for w/  1`] = `
+"const _Vue = Vue
+
+return function render(_ctx, _cache) {
+  with (_ctx) {
+    const { renderList: _renderList, Fragment: _Fragment, openBlock: _openBlock, createElementBlock: _createElementBlock, renderSlot: _renderSlot } = _Vue
+
+    return (_openBlock(true), _createElementBlock(_Fragment, null, _renderList(items, (item) => {
+      return _renderSlot($slots, "default")
+    }), 256 /* UNKEYED_FRAGMENT */))
+  }
+}"
+`;
+
+exports[`compiler: v-for > codegen > v-for on  1`] = `
+"const _Vue = Vue
+
+return function render(_ctx, _cache) {
+  with (_ctx) {
+    const { renderList: _renderList, Fragment: _Fragment, openBlock: _openBlock, createElementBlock: _createElementBlock, renderSlot: _renderSlot } = _Vue
+
+    return (_openBlock(true), _createElementBlock(_Fragment, null, _renderList(items, (item) => {
+      return _renderSlot($slots, "default")
+    }), 256 /* UNKEYED_FRAGMENT */))
+  }
+}"
+`;
+
+exports[`compiler: v-for > codegen > v-for on element with custom directive 1`] = `
+"const _Vue = Vue
+
+return function render(_ctx, _cache) {
+  with (_ctx) {
+    const { renderList: _renderList, Fragment: _Fragment, openBlock: _openBlock, createElementBlock: _createElementBlock, resolveDirective: _resolveDirective, withDirectives: _withDirectives } = _Vue
+
+    const _directive_foo = _resolveDirective("foo")
+
+    return (_openBlock(true), _createElementBlock(_Fragment, null, _renderList(list, (i) => {
+      return _withDirectives((_openBlock(), _createElementBlock("div", null, null, 512 /* NEED_PATCH */)), [
+        [_directive_foo]
+      ])
+    }), 256 /* UNKEYED_FRAGMENT */))
+  }
+}"
+`;
+
+exports[`compiler: v-for > codegen > v-for with constant expression 1`] = `
+"const _Vue = Vue
+
+return function render(_ctx, _cache) {
+  with (_ctx) {
+    const { renderList: _renderList, Fragment: _Fragment, openBlock: _openBlock, createElementBlock: _createElementBlock, toDisplayString: _toDisplayString, createElementVNode: _createElementVNode } = _Vue
+
+    return (_openBlock(), _createElementBlock(_Fragment, null, _renderList(10, (item) => {
+      return _createElementVNode("p", null, _toDisplayString(item), 1 /* TEXT */)
+    }), 64 /* STABLE_FRAGMENT */))
+  }
+}"
+`;
+
+exports[`compiler: v-for > codegen > v-if + v-for 1`] = `
+"const _Vue = Vue
+
+return function render(_ctx, _cache) {
+  with (_ctx) {
+    const { renderList: _renderList, Fragment: _Fragment, openBlock: _openBlock, createElementBlock: _createElementBlock, createCommentVNode: _createCommentVNode } = _Vue
+
+    return ok
+      ? (_openBlock(true), _createElementBlock(_Fragment, { key: 0 }, _renderList(list, (i) => {
+          return (_openBlock(), _createElementBlock("div"))
+        }), 256 /* UNKEYED_FRAGMENT */))
+      : _createCommentVNode("v-if", true)
+  }
+}"
+`;
+
+exports[`compiler: v-for > codegen > v-if + v-for on