diff --git a/.eslintignore b/.eslintignore deleted file mode 100644 index 4d4f41e301f..00000000000 --- 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 0a44fb79aa9..00000000000 --- a/.eslintrc.cjs +++ /dev/null @@ -1,140 +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, - { - 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/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/**'], - 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 fa130ca409a..e87294b5b1d 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/CHANGELOG.md b/CHANGELOG.md index 4eae200db69..3aed604979b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,21 @@ +## [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) diff --git a/SECURITY.md b/SECURITY.md index 41a58da2970..743dfa17d90 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -10,4 +10,5 @@ Please note that we do not consider XSS via template expressions a valid attack We would like to thank the following security researchers for responsibly disclosing security issues to us. -- Jeet Pal - [GitHub](https://github.com/jeetpal2007) | [Email](jeetpal2007@gmail.com) | [LinkedIn](https://in.linkedin.com/in/jeet-pal-22601a290 ) +- Jeet Pal - [@jeetpal2007](https://github.com/jeetpal2007) | [Email](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 00000000000..3587a815067 --- /dev/null +++ b/eslint.config.js @@ -0,0 +1,168 @@ +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/**'], + 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 66c831a7b8c..be02ea8be2a 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "private": true, - "version": "3.4.23", - "packageManager": "pnpm@8.15.6", + "version": "3.4.24", + "packageManager": "pnpm@9.0.5", "type": "module", "scripts": { "dev": "node scripts/dev.js", @@ -13,7 +13,7 @@ "size-esm-runtime": "node scripts/build.js vue -f esm-bundler-runtime", "size-esm": "node scripts/build.js runtime-dom runtime-core reactivity shared -f esm-bundler", "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", @@ -59,7 +59,7 @@ "node": ">=18.12.0" }, "devDependencies": { - "@babel/parser": "^7.24.1", + "@babel/parser": "^7.24.4", "@babel/types": "^7.24.0", "@codspeed/vitest-plugin": "^3.1.0", "@rollup/plugin-alias": "^5.1.0", @@ -70,49 +70,58 @@ "@rollup/plugin-terser": "^0.4.4", "@types/hash-sum": "^1.0.2", "@types/minimist": "^1.2.5", - "@types/node": "^20.12.5", + "@types/node": "^20.12.7", "@types/semver": "^7.5.8", - "@typescript-eslint/eslint-plugin": "^7.4.0", - "@typescript-eslint/parser": "^7.4.0", - "@vitest/coverage-istanbul": "^1.4.0", + "@vitest/coverage-istanbul": "^1.5.0", "@vue/consolidate": "1.0.0", "conventional-changelog-cli": "^4.1.0", "enquirer": "^2.4.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.8", + "magic-string": "^0.30.10", "markdown-table": "^3.0.3", - "marked": "^12.0.1", + "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.6.3", + "puppeteer": "~22.6.5", "rimraf": "^5.0.5", - "rollup": "^4.13.2", + "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.11.1", - "terser": "^5.30.1", + "terser": "^5.30.3", "todomvc-app-css": "^2.4.3", "tslib": "^2.6.2", "tsx": "^4.7.2", "typescript": "~5.4.5", - "vite": "^5.2.7", - "vitest": "^1.4.0" + "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 429a7968616..22fb209cfb9 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', () => { 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 00000000000..3da778eb675 --- /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