chore: Merge branch 'main' into minor

This commit is contained in:
Evan You 2023-12-04 23:17:41 +08:00
commit e12b10ac3e
50 changed files with 930 additions and 279 deletions

View File

@ -1,3 +1,34 @@
## [3.3.10](https://github.com/vuejs/core/compare/v3.3.9...v3.3.10) (2023-12-04)
### Bug Fixes
* **app:** prevent template from being cached between apps with different options ([#9724](https://github.com/vuejs/core/issues/9724)) ([ec71585](https://github.com/vuejs/core/commit/ec715854ca12520b2afc9e9b3981cbae05ae5206)), closes [#9618](https://github.com/vuejs/core/issues/9618)
* **compiler-sfc:** avoid passing forEach index to genMap ([f12db7f](https://github.com/vuejs/core/commit/f12db7fb564a534cef2e5805cc9f54afe5d72fbf))
* **compiler-sfc:** deindent pug/jade templates ([6345197](https://github.com/vuejs/core/commit/634519720a21fb5a6871454e1cadad7053a568b8)), closes [#3231](https://github.com/vuejs/core/issues/3231) [#3842](https://github.com/vuejs/core/issues/3842) [#7723](https://github.com/vuejs/core/issues/7723)
* **compiler-sfc:** fix :where and :is selector in scoped mode with multiple selectors ([#9735](https://github.com/vuejs/core/issues/9735)) ([c3e2c55](https://github.com/vuejs/core/commit/c3e2c556b532656b50b8ab5cd2d9eabc26622d63)), closes [#9707](https://github.com/vuejs/core/issues/9707)
* **compiler-sfc:** generate more treeshaking friendly code ([#9507](https://github.com/vuejs/core/issues/9507)) ([8d74ca0](https://github.com/vuejs/core/commit/8d74ca0e6fa2738ca6854b7e879ff59419f948c7)), closes [#9500](https://github.com/vuejs/core/issues/9500)
* **compiler-sfc:** support inferring generic types ([#8511](https://github.com/vuejs/core/issues/8511)) ([eb5e307](https://github.com/vuejs/core/commit/eb5e307c0be62002e62c4c800d0dfacb39b0d4ca)), closes [#8482](https://github.com/vuejs/core/issues/8482)
* **compiler-sfc:** support resolving components from props ([#8785](https://github.com/vuejs/core/issues/8785)) ([7cbcee3](https://github.com/vuejs/core/commit/7cbcee3d831241a8bd3588ae92d3f27e3641e25f))
* **compiler-sfc:** throw error when failing to load TS during type resolution ([#8883](https://github.com/vuejs/core/issues/8883)) ([4936d2e](https://github.com/vuejs/core/commit/4936d2e11a8d0ca3704bfe408548cb26bb3fd5e9))
* **cssVars:** cssVar names should be double-escaped when generating code for ssr ([#8824](https://github.com/vuejs/core/issues/8824)) ([5199a12](https://github.com/vuejs/core/commit/5199a12f8855cd06f24bf355708b5a2134f63176)), closes [#7823](https://github.com/vuejs/core/issues/7823)
* **deps:** update compiler to ^7.23.4 ([#9681](https://github.com/vuejs/core/issues/9681)) ([31f6ebc](https://github.com/vuejs/core/commit/31f6ebc4df84490ed29fb75e7bf4259200eb51f0))
* **runtime-core:** Suspense get anchor properly in Transition ([#9309](https://github.com/vuejs/core/issues/9309)) ([65f3fe2](https://github.com/vuejs/core/commit/65f3fe273127a8b68e1222fbb306d28d85f01757)), closes [#8105](https://github.com/vuejs/core/issues/8105)
* **runtime-dom:** set width/height with units as attribute ([#8781](https://github.com/vuejs/core/issues/8781)) ([bfc1838](https://github.com/vuejs/core/commit/bfc1838f31199de3f189198a3c234fa7bae91386))
* **ssr:** avoid computed being accidentally cached before server render ([#9688](https://github.com/vuejs/core/issues/9688)) ([30d5d93](https://github.com/vuejs/core/commit/30d5d93a92b2154406ec04f8aca6b217fa01177c)), closes [#5300](https://github.com/vuejs/core/issues/5300)
* **types:** expose emits as props in functional components ([#9234](https://github.com/vuejs/core/issues/9234)) ([887e54c](https://github.com/vuejs/core/commit/887e54c347ea9eac4c721b5e2288f054873d1d30))
* **types:** fix reactive collection types ([#8960](https://github.com/vuejs/core/issues/8960)) ([ad27473](https://github.com/vuejs/core/commit/ad274737015c36906d76f3189203093fa3a2e4e7)), closes [#8904](https://github.com/vuejs/core/issues/8904)
* **types:** improve return type withKeys and withModifiers ([#9734](https://github.com/vuejs/core/issues/9734)) ([43c3cfd](https://github.com/vuejs/core/commit/43c3cfdec5ae5d70fa2a21e857abc2d73f1a0d07))
### Performance Improvements
* optimize on* prop check ([38aaa8c](https://github.com/vuejs/core/commit/38aaa8c88648c54fe2616ad9c0961288092fcb44))
* **runtime-dom:** cache modifier wrapper functions ([da4a4fb](https://github.com/vuejs/core/commit/da4a4fb5e8eee3c6d31f24ebd79a9d0feca56cb2)), closes [#8882](https://github.com/vuejs/core/issues/8882)
* **v-on:** constant handlers with modifiers should not be treated as dynamic ([4d94ebf](https://github.com/vuejs/core/commit/4d94ebfe75174b340d2b794e699cad1add3600a9))
# [3.4.0-alpha.3](https://github.com/vuejs/core/compare/v3.4.0-alpha.2...v3.4.0-alpha.3) (2023-11-28) # [3.4.0-alpha.3](https://github.com/vuejs/core/compare/v3.4.0-alpha.2...v3.4.0-alpha.3) (2023-11-28)
@ -97,7 +128,7 @@
* **compiler-sfc:** fix dynamic directive arguments usage check for slots ([#9495](https://github.com/vuejs/core/issues/9495)) ([b39fa1f](https://github.com/vuejs/core/commit/b39fa1f8157647859331ce439c42ae016a49b415)), closes [#9493](https://github.com/vuejs/core/issues/9493) * **compiler-sfc:** fix dynamic directive arguments usage check for slots ([#9495](https://github.com/vuejs/core/issues/9495)) ([b39fa1f](https://github.com/vuejs/core/commit/b39fa1f8157647859331ce439c42ae016a49b415)), closes [#9493](https://github.com/vuejs/core/issues/9493)
* **deps:** update dependency @vue/repl to ^2.6.2 ([#9536](https://github.com/vuejs/core/issues/9536)) ([5cef325](https://github.com/vuejs/core/commit/5cef325f41e3b38657c72fa1a38dedeee1c7a60a)) * **deps:** update dependency @vue/repl to ^2.6.2 ([#9536](https://github.com/vuejs/core/issues/9536)) ([5cef325](https://github.com/vuejs/core/commit/5cef325f41e3b38657c72fa1a38dedeee1c7a60a))
* **deps:** update dependency @vue/repl to ^2.6.3 ([#9540](https://github.com/vuejs/core/issues/9540)) ([176d590](https://github.com/vuejs/core/commit/176d59058c9aecffe9da4d4311e98496684f06d4)) * **deps:** update dependency @vue/repl to ^2.6.3 ([#9540](https://github.com/vuejs/core/issues/9540)) ([176d590](https://github.com/vuejs/core/commit/176d59058c9aecffe9da4d4311e98496684f06d4))
* **hydration:** fix tagName access eeror on comment/text node hydration mismatch ([dd8a0cf](https://github.com/vuejs/core/commit/dd8a0cf5dcde13d2cbd899262a0e07f16e14e489)), closes [#9531](https://github.com/vuejs/core/issues/9531) * **hydration:** fix tagName access error on comment/text node hydration mismatch ([dd8a0cf](https://github.com/vuejs/core/commit/dd8a0cf5dcde13d2cbd899262a0e07f16e14e489)), closes [#9531](https://github.com/vuejs/core/issues/9531)
* **types:** avoid exposing lru-cache types in generated dts ([462aeb3](https://github.com/vuejs/core/commit/462aeb3b600765e219ded2ee9a0ed1e74df61de0)), closes [#9521](https://github.com/vuejs/core/issues/9521) * **types:** avoid exposing lru-cache types in generated dts ([462aeb3](https://github.com/vuejs/core/commit/462aeb3b600765e219ded2ee9a0ed1e74df61de0)), closes [#9521](https://github.com/vuejs/core/issues/9521)
* **warn:** avoid warning on empty children with Suspense ([#3962](https://github.com/vuejs/core/issues/3962)) ([405f345](https://github.com/vuejs/core/commit/405f34587a63a5f1e3d147b9848219ea98acc22d)) * **warn:** avoid warning on empty children with Suspense ([#3962](https://github.com/vuejs/core/issues/3962)) ([405f345](https://github.com/vuejs/core/commit/405f34587a63a5f1e3d147b9848219ea98acc22d))

View File

@ -57,8 +57,8 @@
"node": ">=18.12.0" "node": ">=18.12.0"
}, },
"devDependencies": { "devDependencies": {
"@babel/parser": "^7.23.4", "@babel/parser": "^7.23.5",
"@babel/types": "^7.23.4", "@babel/types": "^7.23.5",
"@rollup/plugin-alias": "^5.0.1", "@rollup/plugin-alias": "^5.0.1",
"@rollup/plugin-commonjs": "^25.0.7", "@rollup/plugin-commonjs": "^25.0.7",
"@rollup/plugin-json": "^6.0.1", "@rollup/plugin-json": "^6.0.1",
@ -67,7 +67,7 @@
"@rollup/plugin-terser": "^0.4.4", "@rollup/plugin-terser": "^0.4.4",
"@types/hash-sum": "^1.0.2", "@types/hash-sum": "^1.0.2",
"@types/minimist": "^1.2.5", "@types/minimist": "^1.2.5",
"@types/node": "^20.10.0", "@types/node": "^20.10.3",
"@types/semver": "^7.5.5", "@types/semver": "^7.5.5",
"@typescript-eslint/parser": "^6.13.0", "@typescript-eslint/parser": "^6.13.0",
"@vitest/coverage-istanbul": "^0.34.6", "@vitest/coverage-istanbul": "^0.34.6",
@ -93,7 +93,7 @@
"prettier": "^3.1.0", "prettier": "^3.1.0",
"pretty-bytes": "^6.1.1", "pretty-bytes": "^6.1.1",
"pug": "^3.0.2", "pug": "^3.0.2",
"puppeteer": "~21.5.1", "puppeteer": "~21.5.2",
"rimraf": "^5.0.5", "rimraf": "^5.0.5",
"rollup": "^4.1.4", "rollup": "^4.1.4",
"rollup-plugin-dts": "^6.1.0", "rollup-plugin-dts": "^6.1.0",
@ -105,7 +105,7 @@
"terser": "^5.22.0", "terser": "^5.22.0",
"todomvc-app-css": "^2.4.3", "todomvc-app-css": "^2.4.3",
"tslib": "^2.6.2", "tslib": "^2.6.2",
"tsx": "^4.5.0", "tsx": "^4.6.2",
"typescript": "^5.2.2", "typescript": "^5.2.2",
"vite": "^5.0.0", "vite": "^5.0.0",
"vitest": "^0.34.6" "vitest": "^0.34.6"

View File

@ -152,6 +152,28 @@ describe('compiler: element transform', () => {
expect(node.tag).toBe(`Foo.Example`) expect(node.tag).toBe(`Foo.Example`)
}) })
test('resolve namespaced component from props bindings (inline)', () => {
const { root, node } = parseWithElementTransform(`<Foo.Example/>`, {
inline: true,
bindingMetadata: {
Foo: BindingTypes.PROPS
}
})
expect(root.helpers).not.toContain(RESOLVE_COMPONENT)
expect(node.tag).toBe(`_unref(__props["Foo"]).Example`)
})
test('resolve namespaced component from props bindings (non-inline)', () => {
const { root, node } = parseWithElementTransform(`<Foo.Example/>`, {
inline: false,
bindingMetadata: {
Foo: BindingTypes.PROPS
}
})
expect(root.helpers).not.toContain(RESOLVE_COMPONENT)
expect(node.tag).toBe('_unref($props["Foo"]).Example')
})
test('do not resolve component from non-script-setup bindings', () => { test('do not resolve component from non-script-setup bindings', () => {
const bindingMetadata = { const bindingMetadata = {
Example: BindingTypes.SETUP_MAYBE_REF Example: BindingTypes.SETUP_MAYBE_REF
@ -1138,6 +1160,20 @@ describe('compiler: element transform', () => {
genFlagText([PatchFlags.PROPS, PatchFlags.NEED_HYDRATION]) genFlagText([PatchFlags.PROPS, PatchFlags.NEED_HYDRATION])
) )
}) })
test('should not have PROPS patchflag for constant v-on handlers', () => {
const { node } = parseWithElementTransform(`<div @keydown="foo" />`, {
prefixIdentifiers: true,
bindingMetadata: {
foo: BindingTypes.SETUP_CONST
},
directiveTransforms: {
on: transformOn
}
})
// should only have hydration flag
expect(node.patchFlag).toBe(genFlagText(PatchFlags.NEED_HYDRATION))
})
}) })
describe('dynamic component', () => { describe('dynamic component', () => {

View File

@ -32,13 +32,13 @@
}, },
"homepage": "https://github.com/vuejs/core/tree/main/packages/compiler-core#readme", "homepage": "https://github.com/vuejs/core/tree/main/packages/compiler-core#readme",
"dependencies": { "dependencies": {
"@babel/parser": "^7.23.4", "@babel/parser": "^7.23.5",
"@vue/shared": "workspace:*", "@vue/shared": "workspace:*",
"entities": "^4.5.0", "entities": "^4.5.0",
"estree-walker": "^2.0.2", "estree-walker": "^2.0.2",
"source-map-js": "^1.0.2" "source-map-js": "^1.0.2"
}, },
"devDependencies": { "devDependencies": {
"@babel/types": "^7.23.4" "@babel/types": "^7.23.5"
} }
} }

View File

@ -19,7 +19,8 @@ import {
TemplateTextChildNode, TemplateTextChildNode,
DirectiveArguments, DirectiveArguments,
createVNodeCall, createVNodeCall,
ConstantTypes ConstantTypes,
JSChildNode
} from '../ast' } from '../ast'
import { import {
PatchFlags, PatchFlags,
@ -370,6 +371,13 @@ function resolveSetupReference(name: string, context: TransformContext) {
`${context.helperString(UNREF)}(${fromMaybeRef})` `${context.helperString(UNREF)}(${fromMaybeRef})`
: `$setup[${JSON.stringify(fromMaybeRef)}]` : `$setup[${JSON.stringify(fromMaybeRef)}]`
} }
const fromProps = checkType(BindingTypes.PROPS)
if (fromProps) {
return `${context.helperString(UNREF)}(${
context.inline ? '__props' : '$props'
}[${JSON.stringify(fromProps)}])`
}
} }
export type PropsExpression = ObjectExpression | CallExpression | ExpressionNode export type PropsExpression = ObjectExpression | CallExpression | ExpressionNode
@ -437,6 +445,12 @@ export function buildProps(
hasVnodeHook = true hasVnodeHook = true
} }
if (isEventHandler && value.type === NodeTypes.JS_CALL_EXPRESSION) {
// handler wrapped with internal helper e.g. withModifiers(fn)
// extract the actual expression
value = value.arguments[0] as JSChildNode
}
if ( if (
value.type === NodeTypes.JS_CACHE_EXPRESSION || value.type === NodeTypes.JS_CACHE_EXPRESSION ||
((value.type === NodeTypes.SIMPLE_EXPRESSION || ((value.type === NodeTypes.SIMPLE_EXPRESSION ||

View File

@ -7,7 +7,8 @@ import {
NodeTypes, NodeTypes,
ObjectExpression, ObjectExpression,
transform, transform,
VNodeCall VNodeCall,
BindingTypes
} from '@vue/compiler-core' } from '@vue/compiler-core'
import { transformOn } from '../../src/transforms/vOn' import { transformOn } from '../../src/transforms/vOn'
import { V_ON_WITH_KEYS, V_ON_WITH_MODIFIERS } from '../../src/runtimeHelpers' import { V_ON_WITH_KEYS, V_ON_WITH_MODIFIERS } from '../../src/runtimeHelpers'
@ -25,12 +26,11 @@ function parseWithVOn(template: string, options: CompilerOptions = {}) {
}, },
...options ...options
}) })
const node = (ast.children[0] as ElementNode).codegenNode as VNodeCall
return { return {
root: ast, root: ast,
props: ( node,
((ast.children[0] as ElementNode).codegenNode as VNodeCall) props: (node.props as ObjectExpression).properties
.props as ObjectExpression
).properties
} }
} }
@ -288,4 +288,18 @@ describe('compiler-dom: transform v-on', () => {
} }
}) })
}) })
test('should not have PROPS patchFlag for constant v-on handlers with modifiers', () => {
const { node } = parseWithVOn(`<div @keydown.up="foo" />`, {
prefixIdentifiers: true,
bindingMetadata: {
foo: BindingTypes.SETUP_CONST
},
directiveTransforms: {
on: transformOn
}
})
// should only have hydration flag
expect(node.patchFlag).toBe(genFlagText(PatchFlags.NEED_HYDRATION))
})
}) })

View File

@ -1065,10 +1065,12 @@ export default {
setup(__props) { setup(__props) {
const count = ref(0) const count = ref(0)
const style = { color: 'red' }
return (_ctx, _push, _parent, _attrs) => { return (_ctx, _push, _parent, _attrs) => {
const _cssVars = { style: { const _cssVars = { style: {
\\"--xxxxxxxx-count\\": (count.value) \\"--xxxxxxxx-count\\": (count.value),
\\"--xxxxxxxx-style\\\\\\\\.color\\": (style.color)
}} }}
_push(\`<!--[--><div\${ _push(\`<!--[--><div\${
_ssrRenderAttrs(_cssVars) _ssrRenderAttrs(_cssVars)

View File

@ -826,6 +826,7 @@ describe('SFC compile <script setup>', () => {
<script setup> <script setup>
import { ref } from 'vue' import { ref } from 'vue'
const count = ref(0) const count = ref(0)
const style = { color: 'red' }
</script> </script>
<template> <template>
<div>{{ count }}</div> <div>{{ count }}</div>
@ -833,6 +834,7 @@ describe('SFC compile <script setup>', () => {
</template> </template>
<style> <style>
div { color: v-bind(count) } div { color: v-bind(count) }
span { color: v-bind(style.color) }
</style> </style>
`, `,
{ {
@ -847,6 +849,7 @@ describe('SFC compile <script setup>', () => {
expect(content).toMatch(`ssrInterpolate`) expect(content).toMatch(`ssrInterpolate`)
expect(content).not.toMatch(`useCssVars`) expect(content).not.toMatch(`useCssVars`)
expect(content).toMatch(`"--${mockId}-count": (count.value)`) expect(content).toMatch(`"--${mockId}-count": (count.value)`)
expect(content).toMatch(`"--${mockId}-style\\\\.color": (style.color)`)
assertCode(content) assertCode(content)
}) })

View File

@ -27,7 +27,7 @@ exports[`defineModel() > w/ array props 1`] = `
"import { useModel as _useModel, mergeModels as _mergeModels } from 'vue' "import { useModel as _useModel, mergeModels as _mergeModels } from 'vue'
export default { export default {
props: _mergeModels(['foo', 'bar'], { props: /*#__PURE__*/_mergeModels(['foo', 'bar'], {
\\"count\\": {}, \\"count\\": {},
}), }),
emits: [\\"update:count\\"], emits: [\\"update:count\\"],
@ -47,10 +47,10 @@ exports[`defineModel() > w/ defineProps and defineEmits 1`] = `
"import { useModel as _useModel, mergeModels as _mergeModels } from 'vue' "import { useModel as _useModel, mergeModels as _mergeModels } from 'vue'
export default { export default {
props: _mergeModels({ foo: String }, { props: /*#__PURE__*/_mergeModels({ foo: String }, {
\\"modelValue\\": { default: 0 }, \\"modelValue\\": { default: 0 },
}), }),
emits: _mergeModels(['change'], [\\"update:modelValue\\"]), emits: /*#__PURE__*/_mergeModels(['change'], [\\"update:modelValue\\"]),
setup(__props, { expose: __expose }) { setup(__props, { expose: __expose }) {
__expose(); __expose();

View File

@ -332,7 +332,7 @@ exports[`defineProps > withDefaults (dynamic) 1`] = `
import { defaults } from './foo' import { defaults } from './foo'
export default /*#__PURE__*/_defineComponent({ export default /*#__PURE__*/_defineComponent({
props: _mergeDefaults({ props: /*#__PURE__*/_mergeDefaults({
foo: { type: String, required: false }, foo: { type: String, required: false },
bar: { type: Number, required: false }, bar: { type: Number, required: false },
baz: { type: Boolean, required: true } baz: { type: Boolean, required: true }
@ -353,7 +353,7 @@ exports[`defineProps > withDefaults (dynamic) w/ production mode 1`] = `
import { defaults } from './foo' import { defaults } from './foo'
export default /*#__PURE__*/_defineComponent({ export default /*#__PURE__*/_defineComponent({
props: _mergeDefaults({ props: /*#__PURE__*/_mergeDefaults({
foo: { type: Function }, foo: { type: Function },
bar: { type: Boolean }, bar: { type: Boolean },
baz: { type: [Boolean, Function] }, baz: { type: [Boolean, Function] },
@ -375,7 +375,7 @@ exports[`defineProps > withDefaults (reference) 1`] = `
import { defaults } from './foo' import { defaults } from './foo'
export default /*#__PURE__*/_defineComponent({ export default /*#__PURE__*/_defineComponent({
props: _mergeDefaults({ props: /*#__PURE__*/_mergeDefaults({
foo: { type: String, required: false }, foo: { type: String, required: false },
bar: { type: Number, required: false }, bar: { type: Number, required: false },
baz: { type: Boolean, required: true } baz: { type: Boolean, required: true }
@ -462,7 +462,7 @@ exports[`defineProps > withDefaults w/ dynamic object method 1`] = `
"import { mergeDefaults as _mergeDefaults, defineComponent as _defineComponent } from 'vue' "import { mergeDefaults as _mergeDefaults, defineComponent as _defineComponent } from 'vue'
export default /*#__PURE__*/_defineComponent({ export default /*#__PURE__*/_defineComponent({
props: _mergeDefaults({ props: /*#__PURE__*/_mergeDefaults({
foo: { type: Function, required: false } foo: { type: Function, required: false }
}, { }, {
['fo' + 'o']() { return 'foo' } ['fo' + 'o']() { return 'foo' }

View File

@ -62,7 +62,7 @@ exports[`sfc reactive props destructure > default values w/ array runtime declar
"import { mergeDefaults as _mergeDefaults } from 'vue' "import { mergeDefaults as _mergeDefaults } from 'vue'
export default { export default {
props: _mergeDefaults(['foo', 'bar', 'baz'], { props: /*#__PURE__*/_mergeDefaults(['foo', 'bar', 'baz'], {
foo: 1, foo: 1,
bar: () => ({}), bar: () => ({}),
func: () => {}, __skip_func: true func: () => {}, __skip_func: true
@ -81,7 +81,7 @@ exports[`sfc reactive props destructure > default values w/ object runtime decla
"import { mergeDefaults as _mergeDefaults } from 'vue' "import { mergeDefaults as _mergeDefaults } from 'vue'
export default { export default {
props: _mergeDefaults({ foo: Number, bar: Object, func: Function, ext: null }, { props: /*#__PURE__*/_mergeDefaults({ foo: Number, bar: Object, func: Function, ext: null }, {
foo: 1, foo: 1,
bar: () => ({}), bar: () => ({}),
func: () => {}, __skip_func: true, func: () => {}, __skip_func: true,
@ -101,7 +101,7 @@ exports[`sfc reactive props destructure > default values w/ runtime declaration
"import { mergeDefaults as _mergeDefaults } from 'vue' "import { mergeDefaults as _mergeDefaults } from 'vue'
export default { export default {
props: _mergeDefaults(['foo', 'foo:bar'], { props: /*#__PURE__*/_mergeDefaults(['foo', 'foo:bar'], {
foo: 1, foo: 1,
\\"foo:bar\\": 'foo-bar' \\"foo:bar\\": 'foo-bar'
}), }),

View File

@ -48,7 +48,7 @@ describe('defineModel()', () => {
{ defineModel: true } { defineModel: true }
) )
assertCode(content) assertCode(content)
expect(content).toMatch(`props: _mergeModels({ foo: String }`) expect(content).toMatch(`props: /*#__PURE__*/_mergeModels({ foo: String }`)
expect(content).toMatch(`"modelValue": { default: 0 }`) expect(content).toMatch(`"modelValue": { default: 0 }`)
expect(content).toMatch(`const count = _useModel(__props, "modelValue")`) expect(content).toMatch(`const count = _useModel(__props, "modelValue")`)
expect(content).not.toMatch('defineModel') expect(content).not.toMatch('defineModel')
@ -70,7 +70,7 @@ describe('defineModel()', () => {
{ defineModel: true } { defineModel: true }
) )
assertCode(content) assertCode(content)
expect(content).toMatch(`props: _mergeModels(['foo', 'bar'], { expect(content).toMatch(`props: /*#__PURE__*/_mergeModels(['foo', 'bar'], {
"count": {}, "count": {},
})`) })`)
expect(content).toMatch(`const count = _useModel(__props, "count")`) expect(content).toMatch(`const count = _useModel(__props, "count")`)

View File

@ -78,7 +78,8 @@ describe('sfc reactive props destructure', () => {
// literals can be used as-is, non-literals are always returned from a // literals can be used as-is, non-literals are always returned from a
// function // function
// functions need to be marked with a skip marker // functions need to be marked with a skip marker
expect(content).toMatch(`props: _mergeDefaults(['foo', 'bar', 'baz'], { expect(content)
.toMatch(`props: /*#__PURE__*/_mergeDefaults(['foo', 'bar', 'baz'], {
foo: 1, foo: 1,
bar: () => ({}), bar: () => ({}),
func: () => {}, __skip_func: true func: () => {}, __skip_func: true
@ -98,7 +99,7 @@ describe('sfc reactive props destructure', () => {
// safely infer whether runtime type is Function (e.g. if the runtime decl // safely infer whether runtime type is Function (e.g. if the runtime decl
// is imported, or spreads another object) // is imported, or spreads another object)
expect(content) expect(content)
.toMatch(`props: _mergeDefaults({ foo: Number, bar: Object, func: Function, ext: null }, { .toMatch(`props: /*#__PURE__*/_mergeDefaults({ foo: Number, bar: Object, func: Function, ext: null }, {
foo: 1, foo: 1,
bar: () => ({}), bar: () => ({}),
func: () => {}, __skip_func: true, func: () => {}, __skip_func: true,
@ -122,7 +123,7 @@ describe('sfc reactive props destructure', () => {
}) })
expect(content).toMatch(` expect(content).toMatch(`
props: _mergeDefaults(['foo', 'foo:bar'], { props: /*#__PURE__*/_mergeDefaults(['foo', 'foo:bar'], {
foo: 1, foo: 1,
"foo:bar": 'foo-bar' "foo:bar": 'foo-bar'
}),`) }),`)

View File

@ -455,6 +455,88 @@ describe('resolveType', () => {
}) })
}) })
describe('generics', () => {
test('generic with type literal', () => {
expect(
resolve(`
type Props<T> = T
defineProps<Props<{ foo: string }>>()
`).props
).toStrictEqual({
foo: ['String']
})
})
test('generic used in intersection', () => {
expect(
resolve(`
type Foo = { foo: string; }
type Bar = { bar: number; }
type Props<T,U> = T & U & { baz: boolean }
defineProps<Props<Foo, Bar>>()
`).props
).toStrictEqual({
foo: ['String'],
bar: ['Number'],
baz: ['Boolean']
})
})
test('generic type /w generic type alias', () => {
expect(
resolve(`
type Aliased<T> = Readonly<Partial<T>>
type Props<T> = Aliased<T>
type Foo = { foo: string; }
defineProps<Props<Foo>>()
`).props
).toStrictEqual({
foo: ['String']
})
})
test('generic type /w aliased type literal', () => {
expect(
resolve(`
type Aliased<T> = { foo: T }
defineProps<Aliased<string>>()
`).props
).toStrictEqual({
foo: ['String']
})
})
test('generic type /w interface', () => {
expect(
resolve(`
interface Props<T> {
foo: T
}
type Foo = string
defineProps<Props<Foo>>()
`).props
).toStrictEqual({
foo: ['String']
})
})
test('generic from external-file', () => {
const files = {
'/foo.ts': 'export type P<T> = { foo: T }'
}
const { props } = resolve(
`
import { P } from './foo'
defineProps<P<string>>()
`,
files
)
expect(props).toStrictEqual({
foo: ['String']
})
})
})
describe('external type imports', () => { describe('external type imports', () => {
test('relative ts', () => { test('relative ts', () => {
const files = { const files = {

View File

@ -144,6 +144,23 @@ describe('SFC scoped CSS', () => {
`) `)
}) })
test(':is() and :where() with multiple selectors', () => {
expect(compileScoped(`:is(.foo) { color: red; }`)).toMatchInlineSnapshot(`
":is(.foo[data-v-test]) { color: red;
}"
`)
expect(compileScoped(`:where(.foo, .bar) { color: red; }`))
.toMatchInlineSnapshot(`
":where(.foo[data-v-test], .bar[data-v-test]) { color: red;
}"
`)
expect(compileScoped(`:is(.foo, .bar) div { color: red; }`))
.toMatchInlineSnapshot(`
":is(.foo, .bar) div[data-v-test] { color: red;
}"
`)
})
test('media query', () => { test('media query', () => {
expect(compileScoped(`@media print { .foo { color: red }}`)) expect(compileScoped(`@media print { .foo { color: red }}`))
.toMatchInlineSnapshot(` .toMatchInlineSnapshot(`

View File

@ -61,6 +61,33 @@ body
expect(result.errors.length).toBe(0) expect(result.errors.length).toBe(0)
}) })
test('preprocess pug with indents and blank lines', () => {
const template = parse(
`
<template lang="pug">
body
h1 The next line contains four spaces.
div.container
p The next line is empty.
p This is the last line.
</template>
`,
{ filename: 'example.vue', sourceMap: true }
).descriptor.template as SFCTemplateBlock
const result = compile({
filename: 'example.vue',
source: template.content,
preprocessLang: template.lang
})
expect(result.errors.length).toBe(0)
expect(result.source).toBe(
'<body><h1>The next line contains four spaces.</h1><div class="container"><p>The next line is empty.</p></div><p>This is the last line.</p></body>'
)
})
test('warn missing preprocessor', () => { test('warn missing preprocessor', () => {
const template = parse(`<template lang="unknownLang">hi</template>\n`, { const template = parse(`<template lang="unknownLang">hi</template>\n`, {
filename: 'example.vue', filename: 'example.vue',

View File

@ -80,6 +80,26 @@ font-weight: bold;
}) })
}) })
test('template block with lang + indent', () => {
// Padding determines how many blank lines will there be before the style block
const padding = Math.round(Math.random() * 10)
const template = parse(
`${'\n'.repeat(padding)}<template lang="pug">
h1 foo
div bar
span baz
</template>\n`
).descriptor.template!
expect(template.map).not.toBeUndefined()
const consumer = new SourceMapConsumer(template.map!)
consumer.eachMapping(mapping => {
expect(mapping.originalLine - mapping.generatedLine).toBe(padding)
expect(mapping.originalColumn - mapping.generatedColumn).toBe(2)
})
})
test('custom block', () => { test('custom block', () => {
const padding = Math.round(Math.random() * 10) const padding = Math.round(Math.random() * 10)
const custom = parse( const custom = parse(

View File

@ -32,18 +32,18 @@
}, },
"homepage": "https://github.com/vuejs/core/tree/main/packages/compiler-sfc#readme", "homepage": "https://github.com/vuejs/core/tree/main/packages/compiler-sfc#readme",
"dependencies": { "dependencies": {
"@babel/parser": "^7.23.4", "@babel/parser": "^7.23.5",
"@vue/compiler-core": "workspace:*", "@vue/compiler-core": "workspace:*",
"@vue/compiler-dom": "workspace:*", "@vue/compiler-dom": "workspace:*",
"@vue/compiler-ssr": "workspace:*", "@vue/compiler-ssr": "workspace:*",
"@vue/shared": "workspace:*", "@vue/shared": "workspace:*",
"estree-walker": "^2.0.2", "estree-walker": "^2.0.2",
"magic-string": "^0.30.5", "magic-string": "^0.30.5",
"postcss": "^8.4.31", "postcss": "^8.4.32",
"source-map-js": "^1.0.2" "source-map-js": "^1.0.2"
}, },
"devDependencies": { "devDependencies": {
"@babel/types": "^7.23.4", "@babel/types": "^7.23.5",
"@vue/consolidate": "^0.17.3", "@vue/consolidate": "^0.17.3",
"hash-sum": "^2.0.0", "hash-sum": "^2.0.0",
"lru-cache": "^10.1.0", "lru-cache": "^10.1.0",

View File

@ -239,22 +239,34 @@ export function parse(
} }
} }
// dedent pug/jade templates
let templateColumnOffset = 0
if (
descriptor.template &&
(descriptor.template.lang === 'pug' || descriptor.template.lang === 'jade')
) {
;[descriptor.template.content, templateColumnOffset] = dedent(
descriptor.template.content
)
}
if (sourceMap) { if (sourceMap) {
const genMap = (block: SFCBlock | null) => { const genMap = (block: SFCBlock | null, columnOffset = 0) => {
if (block && !block.src) { if (block && !block.src) {
block.map = generateSourceMap( block.map = generateSourceMap(
filename, filename,
source, source,
block.content, block.content,
sourceRoot, sourceRoot,
!pad || block.type === 'template' ? block.loc.start.line - 1 : 0 !pad || block.type === 'template' ? block.loc.start.line - 1 : 0,
columnOffset
) )
} }
} }
genMap(descriptor.template) genMap(descriptor.template, templateColumnOffset)
genMap(descriptor.script) genMap(descriptor.script)
descriptor.styles.forEach(genMap) descriptor.styles.forEach(s => genMap(s))
descriptor.customBlocks.forEach(genMap) descriptor.customBlocks.forEach(s => genMap(s))
} }
// parse CSS vars // parse CSS vars
@ -335,7 +347,8 @@ function generateSourceMap(
source: string, source: string,
generated: string, generated: string,
sourceRoot: string, sourceRoot: string,
lineOffset: number lineOffset: number,
columnOffset: number
): RawSourceMap { ): RawSourceMap {
const map = new SourceMapGenerator({ const map = new SourceMapGenerator({
file: filename.replace(/\\/g, '/'), file: filename.replace(/\\/g, '/'),
@ -351,7 +364,7 @@ function generateSourceMap(
if (!/\s/.test(line[i])) { if (!/\s/.test(line[i])) {
map._mappings.add({ map._mappings.add({
originalLine, originalLine,
originalColumn: i, originalColumn: i + columnOffset,
generatedLine, generatedLine,
generatedColumn: i, generatedColumn: i,
source: filename, source: filename,
@ -431,3 +444,31 @@ export function hmrShouldReload(
return false return false
} }
/**
* Dedent a string.
*
* This removes any whitespace that is common to all lines in the string from
* each line in the string.
*/
function dedent(s: string): [string, number] {
const lines = s.split('\n')
const minIndent = lines.reduce(function (minIndent, line) {
if (line.trim() === '') {
return minIndent
}
const indent = line.match(/^\s*/)?.[0]?.length || 0
return Math.min(indent, minIndent)
}, Infinity)
if (minIndent === 0) {
return [s, minIndent]
}
return [
lines
.map(function (line) {
return line.slice(minIndent)
})
.join('\n'),
minIndent
]
}

View File

@ -62,7 +62,9 @@ export function genRuntimeEmits(ctx: ScriptCompileContext): string | undefined {
.map(n => JSON.stringify(`update:${n}`)) .map(n => JSON.stringify(`update:${n}`))
.join(', ')}]` .join(', ')}]`
emitsDecl = emitsDecl emitsDecl = emitsDecl
? `${ctx.helper('mergeModels')}(${emitsDecl}, ${modelEmitsDecl})` ? `/*#__PURE__*/${ctx.helper(
'mergeModels'
)}(${emitsDecl}, ${modelEmitsDecl})`
: modelEmitsDecl : modelEmitsDecl
} }
return emitsDecl return emitsDecl

View File

@ -148,7 +148,7 @@ export function genRuntimeProps(ctx: ScriptCompileContext): string | undefined {
) )
} }
if (defaults.length) { if (defaults.length) {
propsDecls = `${ctx.helper( propsDecls = `/*#__PURE__*/${ctx.helper(
`mergeDefaults` `mergeDefaults`
)}(${propsDecls}, {\n ${defaults.join(',\n ')}\n})` )}(${propsDecls}, {\n ${defaults.join(',\n ')}\n})`
} }
@ -160,7 +160,9 @@ export function genRuntimeProps(ctx: ScriptCompileContext): string | undefined {
const modelsDecls = genModelProps(ctx) const modelsDecls = genModelProps(ctx)
if (propsDecls && modelsDecls) { if (propsDecls && modelsDecls) {
return `${ctx.helper('mergeModels')}(${propsDecls}, ${modelsDecls})` return `/*#__PURE__*/${ctx.helper(
'mergeModels'
)}(${propsDecls}, ${modelsDecls})`
} else { } else {
return modelsDecls || propsDecls return modelsDecls || propsDecls
} }
@ -190,9 +192,9 @@ export function extractRuntimeProps(
${propStrings.join(',\n ')}\n }` ${propStrings.join(',\n ')}\n }`
if (ctx.propsRuntimeDefaults && !hasStaticDefaults) { if (ctx.propsRuntimeDefaults && !hasStaticDefaults) {
propsDecls = `${ctx.helper('mergeDefaults')}(${propsDecls}, ${ctx.getString( propsDecls = `/*#__PURE__*/${ctx.helper(
ctx.propsRuntimeDefaults 'mergeDefaults'
)})` )}(${propsDecls}, ${ctx.getString(ctx.propsRuntimeDefaults)})`
} }
return propsDecls return propsDecls

View File

@ -140,7 +140,8 @@ interface ResolvedElements {
export function resolveTypeElements( export function resolveTypeElements(
ctx: TypeResolveContext, ctx: TypeResolveContext,
node: Node & MaybeWithScope & { _resolvedElements?: ResolvedElements }, node: Node & MaybeWithScope & { _resolvedElements?: ResolvedElements },
scope?: TypeScope scope?: TypeScope,
typeParameters?: Record<string, Node>
): ResolvedElements { ): ResolvedElements {
if (node._resolvedElements) { if (node._resolvedElements) {
return node._resolvedElements return node._resolvedElements
@ -148,30 +149,37 @@ export function resolveTypeElements(
return (node._resolvedElements = innerResolveTypeElements( return (node._resolvedElements = innerResolveTypeElements(
ctx, ctx,
node, node,
node._ownerScope || scope || ctxToScope(ctx) node._ownerScope || scope || ctxToScope(ctx),
typeParameters
)) ))
} }
function innerResolveTypeElements( function innerResolveTypeElements(
ctx: TypeResolveContext, ctx: TypeResolveContext,
node: Node, node: Node,
scope: TypeScope scope: TypeScope,
typeParameters?: Record<string, Node>
): ResolvedElements { ): ResolvedElements {
switch (node.type) { switch (node.type) {
case 'TSTypeLiteral': case 'TSTypeLiteral':
return typeElementsToMap(ctx, node.members, scope) return typeElementsToMap(ctx, node.members, scope, typeParameters)
case 'TSInterfaceDeclaration': case 'TSInterfaceDeclaration':
return resolveInterfaceMembers(ctx, node, scope) return resolveInterfaceMembers(ctx, node, scope, typeParameters)
case 'TSTypeAliasDeclaration': case 'TSTypeAliasDeclaration':
case 'TSParenthesizedType': case 'TSParenthesizedType':
return resolveTypeElements(ctx, node.typeAnnotation, scope) return resolveTypeElements(
ctx,
node.typeAnnotation,
scope,
typeParameters
)
case 'TSFunctionType': { case 'TSFunctionType': {
return { props: {}, calls: [node] } return { props: {}, calls: [node] }
} }
case 'TSUnionType': case 'TSUnionType':
case 'TSIntersectionType': case 'TSIntersectionType':
return mergeElements( return mergeElements(
node.types.map(t => resolveTypeElements(ctx, t, scope)), node.types.map(t => resolveTypeElements(ctx, t, scope, typeParameters)),
node.type node.type
) )
case 'TSMappedType': case 'TSMappedType':
@ -193,20 +201,57 @@ function innerResolveTypeElements(
scope.imports[typeName]?.source === 'vue' scope.imports[typeName]?.source === 'vue'
) { ) {
return resolveExtractPropTypes( return resolveExtractPropTypes(
resolveTypeElements(ctx, node.typeParameters.params[0], scope), resolveTypeElements(
ctx,
node.typeParameters.params[0],
scope,
typeParameters
),
scope scope
) )
} }
const resolved = resolveTypeReference(ctx, node, scope) const resolved = resolveTypeReference(ctx, node, scope)
if (resolved) { if (resolved) {
return resolveTypeElements(ctx, resolved, resolved._ownerScope) const typeParams: Record<string, Node> = Object.create(null)
if (
(resolved.type === 'TSTypeAliasDeclaration' ||
resolved.type === 'TSInterfaceDeclaration') &&
resolved.typeParameters &&
node.typeParameters
) {
resolved.typeParameters.params.forEach((p, i) => {
let param = typeParameters && typeParameters[p.name]
if (!param) param = node.typeParameters!.params[i]
typeParams[p.name] = param
})
}
return resolveTypeElements(
ctx,
resolved,
resolved._ownerScope,
typeParams
)
} else { } else {
if (typeof typeName === 'string') { if (typeof typeName === 'string') {
if (typeParameters && typeParameters[typeName]) {
return resolveTypeElements(
ctx,
typeParameters[typeName],
scope,
typeParameters
)
}
if ( if (
// @ts-ignore // @ts-ignore
SupportedBuiltinsSet.has(typeName) SupportedBuiltinsSet.has(typeName)
) { ) {
return resolveBuiltin(ctx, node, typeName as any, scope) return resolveBuiltin(
ctx,
node,
typeName as any,
scope,
typeParameters
)
} else if (typeName === 'ReturnType' && node.typeParameters) { } else if (typeName === 'ReturnType' && node.typeParameters) {
// limited support, only reference types // limited support, only reference types
const ret = resolveReturnType( const ret = resolveReturnType(
@ -265,11 +310,17 @@ function innerResolveTypeElements(
function typeElementsToMap( function typeElementsToMap(
ctx: TypeResolveContext, ctx: TypeResolveContext,
elements: TSTypeElement[], elements: TSTypeElement[],
scope = ctxToScope(ctx) scope = ctxToScope(ctx),
typeParameters?: Record<string, Node>
): ResolvedElements { ): ResolvedElements {
const res: ResolvedElements = { props: {} } const res: ResolvedElements = { props: {} }
for (const e of elements) { for (const e of elements) {
if (e.type === 'TSPropertySignature' || e.type === 'TSMethodSignature') { if (e.type === 'TSPropertySignature' || e.type === 'TSMethodSignature') {
// capture generic parameters on node's scope
if (typeParameters) {
scope = createChildScope(scope)
Object.assign(scope.types, typeParameters)
}
;(e as MaybeWithScope)._ownerScope = scope ;(e as MaybeWithScope)._ownerScope = scope
const name = getId(e.key) const name = getId(e.key)
if (name && !e.computed) { if (name && !e.computed) {
@ -345,9 +396,15 @@ function createProperty(
function resolveInterfaceMembers( function resolveInterfaceMembers(
ctx: TypeResolveContext, ctx: TypeResolveContext,
node: TSInterfaceDeclaration & MaybeWithScope, node: TSInterfaceDeclaration & MaybeWithScope,
scope: TypeScope scope: TypeScope,
typeParameters?: Record<string, Node>
): ResolvedElements { ): ResolvedElements {
const base = typeElementsToMap(ctx, node.body.body, node._ownerScope) const base = typeElementsToMap(
ctx,
node.body.body,
node._ownerScope,
typeParameters
)
if (node.extends) { if (node.extends) {
for (const ext of node.extends) { for (const ext of node.extends) {
if ( if (
@ -565,9 +622,15 @@ function resolveBuiltin(
ctx: TypeResolveContext, ctx: TypeResolveContext,
node: TSTypeReference | TSExpressionWithTypeArguments, node: TSTypeReference | TSExpressionWithTypeArguments,
name: GetSetType<typeof SupportedBuiltinsSet>, name: GetSetType<typeof SupportedBuiltinsSet>,
scope: TypeScope scope: TypeScope,
typeParameters?: Record<string, Node>
): ResolvedElements { ): ResolvedElements {
const t = resolveTypeElements(ctx, node.typeParameters!.params[0], scope) const t = resolveTypeElements(
ctx,
node.typeParameters!.params[0],
scope,
typeParameters
)
switch (name) { switch (name) {
case 'Partial': { case 'Partial': {
const res: ResolvedElements = { props: {}, calls: t.calls } const res: ResolvedElements = { props: {}, calls: t.calls }
@ -741,7 +804,25 @@ let loadTS: (() => typeof TS) | undefined
* @private * @private
*/ */
export function registerTS(_loadTS: () => typeof TS) { export function registerTS(_loadTS: () => typeof TS) {
loadTS = _loadTS loadTS = () => {
try {
return _loadTS()
} catch (err: any) {
if (
typeof err.message === 'string' &&
err.message.includes('Cannot find module')
) {
throw new Error(
'Failed to load TypeScript, which is required for resolving imported types. ' +
'Please make sure "typescript" is installed as a project dependency.'
)
} else {
throw new Error(
'Failed to load TypeScript for resolving imported types.'
)
}
}
}
} }
type FS = NonNullable<SFCScriptCompileOptions['fs']> type FS = NonNullable<SFCScriptCompileOptions['fs']>
@ -790,7 +871,12 @@ function importSourceToScope(
scope: TypeScope, scope: TypeScope,
source: string source: string
): TypeScope { ): TypeScope {
const fs = resolveFS(ctx) let fs: FS | undefined
try {
fs = resolveFS(ctx)
} catch (err: any) {
return ctx.error(err.message, node, scope)
}
if (!fs) { if (!fs) {
return ctx.error( return ctx.error(
`No fs option provided to \`compileScript\` in non-Node environment. ` + `No fs option provided to \`compileScript\` in non-Node environment. ` +
@ -1102,14 +1188,7 @@ function moduleDeclToScope(
return node._resolvedChildScope return node._resolvedChildScope
} }
const scope = new TypeScope( const scope = createChildScope(parentScope)
parentScope.filename,
parentScope.source,
parentScope.offset,
Object.create(parentScope.imports),
Object.create(parentScope.types),
Object.create(parentScope.declares)
)
if (node.body.type === 'TSModuleDeclaration') { if (node.body.type === 'TSModuleDeclaration') {
const decl = node.body as TSModuleDeclaration & WithScope const decl = node.body as TSModuleDeclaration & WithScope
@ -1123,6 +1202,17 @@ function moduleDeclToScope(
return (node._resolvedChildScope = scope) return (node._resolvedChildScope = scope)
} }
function createChildScope(parentScope: TypeScope) {
return new TypeScope(
parentScope.filename,
parentScope.source,
parentScope.offset,
Object.create(parentScope.imports),
Object.create(parentScope.types),
Object.create(parentScope.declares)
)
}
const importExportRE = /^Import|^Export/ const importExportRE = /^Import|^Export/
function recordTypes( function recordTypes(
@ -1261,7 +1351,7 @@ function recordType(
if (overwriteId || node.id) types[overwriteId || getId(node.id!)] = node if (overwriteId || node.id) types[overwriteId || getId(node.id!)] = node
break break
case 'TSTypeAliasDeclaration': case 'TSTypeAliasDeclaration':
types[node.id.name] = node.typeAnnotation types[node.id.name] = node.typeParameters ? node : node.typeAnnotation
break break
case 'TSDeclareFunction': case 'TSDeclareFunction':
if (node.id) declares[node.id.name] = node if (node.id) declares[node.id.name] = node

View File

@ -121,6 +121,8 @@ export function getEscapedPropName(key: string) {
export const cssVarNameEscapeSymbolsRE = /[ !"#$%&'()*+,./:;<=>?@[\\\]^`{|}~]/g export const cssVarNameEscapeSymbolsRE = /[ !"#$%&'()*+,./:;<=>?@[\\\]^`{|}~]/g
export function getEscapedCssVarName(key: string) { export function getEscapedCssVarName(key: string, doubleEscape: boolean) {
return key.replace(cssVarNameEscapeSymbolsRE, s => `\\${s}`) return key.replace(cssVarNameEscapeSymbolsRE, s =>
doubleEscape ? `\\\\${s}` : `\\${s}`
)
} }

View File

@ -22,17 +22,25 @@ export function genCssVarsFromList(
): string { ): string {
return `{\n ${vars return `{\n ${vars
.map( .map(
key => `"${isSSR ? `--` : ``}${genVarName(id, key, isProd)}": (${key})` key =>
`"${isSSR ? `--` : ``}${genVarName(id, key, isProd, isSSR)}": (${key})`
) )
.join(',\n ')}\n}` .join(',\n ')}\n}`
} }
function genVarName(id: string, raw: string, isProd: boolean): string { function genVarName(
id: string,
raw: string,
isProd: boolean,
isSSR = false
): string {
if (isProd) { if (isProd) {
return hash(id + raw) return hash(id + raw)
} else { } else {
// escape ASCII Punctuation & Symbols // escape ASCII Punctuation & Symbols
return `${id}-${getEscapedCssVarName(raw)}` // #7823 need to double-escape in SSR because the attributes are rendered
// into an HTML string
return `${id}-${getEscapedCssVarName(raw, isSSR)}`
} }
} }

View File

@ -170,15 +170,23 @@ function rewriteSelector(
} }
} }
if (n.type !== 'pseudo' && n.type !== 'combinator') { if (
(n.type !== 'pseudo' && n.type !== 'combinator') ||
(n.type === 'pseudo' && (n.value === ':is' || n.value === ':where'))
) {
node = n node = n
} }
})
if (n.type === 'pseudo' && (n.value === ':is' || n.value === ':where')) { if (node) {
rewriteSelector(id, n.nodes[0], selectorRoot, slotted) const { type, value } = node as selectorParser.Node
if (type === 'pseudo' && (value === ':is' || value === ':where')) {
;(node as selectorParser.Pseudo).nodes.forEach(value =>
rewriteSelector(id, value, selectorRoot, slotted)
)
shouldInject = false shouldInject = false
} }
}) }
if (node) { if (node) {
;(node as selectorParser.Node).spaces.after = '' ;(node as selectorParser.Node).spaces.after = ''

View File

@ -11,7 +11,9 @@ import {
h, h,
SlotsType, SlotsType,
Slots, Slots,
VNode VNode,
withKeys,
withModifiers
} from 'vue' } from 'vue'
import { describe, expectType, IsUnion } from './utils' import { describe, expectType, IsUnion } from './utils'
@ -1497,6 +1499,12 @@ describe('should work when props type is incompatible with setup returned type '
expectType<SizeType>(CompA.$props.size) expectType<SizeType>(CompA.$props.size)
}) })
describe('withKeys and withModifiers as pro', () => {
const onKeydown = withKeys(e => {}, [''])
const onClick = withModifiers(e => {}, [''])
;<input onKeydown={onKeydown} onClick={onClick} />
})
import { import {
DefineComponent, DefineComponent,
ComponentOptionsMixin, ComponentOptionsMixin,

View File

@ -45,7 +45,7 @@ Bar.emits = {
Bar.emits = { baz: () => void 0 } Bar.emits = { baz: () => void 0 }
// TSX // TSX
expectType<JSX.Element>(<Bar foo={1} />) expectType<JSX.Element>(<Bar foo={1} onUpdate={() => {}} />)
// @ts-expect-error // @ts-expect-error
;<Foo /> ;<Foo />
// @ts-expect-error // @ts-expect-error

View File

@ -62,3 +62,31 @@ describe('should unwrap tuple correctly', () => {
const reactiveTuple = reactive(tuple) const reactiveTuple = reactive(tuple)
expectType<Ref<number>>(reactiveTuple[0]) expectType<Ref<number>>(reactiveTuple[0])
}) })
describe('should unwrap Map correctly', () => {
const map = reactive(new Map<string, Ref<number>>())
expectType<Ref<number>>(map.get('a')!)
const map2 = reactive(new Map<string, { wrap: Ref<number> }>())
expectType<number>(map2.get('a')!.wrap)
const wm = reactive(new WeakMap<object, Ref<number>>())
expectType<Ref<number>>(wm.get({})!)
const wm2 = reactive(new WeakMap<object, { wrap: Ref<number> }>())
expectType<number>(wm2.get({})!.wrap)
})
describe('should unwrap Set correctly', () => {
const set = reactive(new Set<Ref<number>>())
expectType<Set<Ref<number>>>(set)
const set2 = reactive(new Set<{ wrap: Ref<number> }>())
expectType<Set<{ wrap: number }>>(set2)
const ws = reactive(new WeakSet<Ref<number>>())
expectType<WeakSet<Ref<number>>>(ws)
const ws2 = reactive(new WeakSet<{ wrap: Ref<number> }>())
expectType<WeakSet<{ wrap: number }>>(ws2)
})

View File

@ -8,7 +8,7 @@ import {
import { ReactiveFlags, TrackOpTypes, TriggerOpTypes } from './constants' import { ReactiveFlags, TrackOpTypes, TriggerOpTypes } from './constants'
import { capitalize, hasOwn, hasChanged, toRawType, isMap } from '@vue/shared' import { capitalize, hasOwn, hasChanged, toRawType, isMap } from '@vue/shared'
export type CollectionTypes = IterableCollections | WeakCollections type CollectionTypes = IterableCollections | WeakCollections
type IterableCollections = Map<any, any> | Set<any> type IterableCollections = Map<any, any> | Set<any>
type WeakCollections = WeakMap<any, any> | WeakSet<any> type WeakCollections = WeakMap<any, any> | WeakSet<any>

View File

@ -16,7 +16,6 @@ import {
isShallow isShallow
} from './reactive' } from './reactive'
import type { ShallowReactiveMarker } from './reactive' import type { ShallowReactiveMarker } from './reactive'
import { CollectionTypes } from './collectionHandlers'
import { createDep, Dep } from './dep' import { createDep, Dep } from './dep'
import { ComputedRefImpl } from './computed' import { ComputedRefImpl } from './computed'
import { getDepFromReactive } from './reactiveEffect' import { getDepFromReactive } from './reactiveEffect'
@ -505,16 +504,23 @@ export type UnwrapRef<T> = T extends ShallowRef<infer V>
export type UnwrapRefSimple<T> = T extends export type UnwrapRefSimple<T> = T extends
| Function | Function
| CollectionTypes
| BaseTypes | BaseTypes
| Ref | Ref
| RefUnwrapBailTypes[keyof RefUnwrapBailTypes] | RefUnwrapBailTypes[keyof RefUnwrapBailTypes]
| { [RawSymbol]?: true } | { [RawSymbol]?: true }
? T ? T
: T extends ReadonlyArray<any> : T extends Map<infer K, infer V>
? { [K in keyof T]: UnwrapRefSimple<T[K]> } ? Map<K, UnwrapRefSimple<V>>
: T extends object & { [ShallowReactiveMarker]?: never } : T extends WeakMap<infer K, infer V>
? { ? WeakMap<K, UnwrapRefSimple<V>>
[P in keyof T]: P extends symbol ? T[P] : UnwrapRef<T[P]> : T extends Set<infer V>
} ? Set<UnwrapRefSimple<V>>
: T : T extends WeakSet<infer V>
? WeakSet<UnwrapRefSimple<V>>
: T extends ReadonlyArray<any>
? { [K in keyof T]: UnwrapRefSimple<T[K]> }
: T extends object & { [ShallowReactiveMarker]?: never }
? {
[P in keyof T]: P extends symbol ? T[P] : UnwrapRef<T[P]>
}
: T

View File

@ -50,7 +50,8 @@ import {
ObjectEmitsOptions, ObjectEmitsOptions,
EmitFn, EmitFn,
emit, emit,
normalizeEmitsOptions normalizeEmitsOptions,
EmitsToProps
} from './componentEmits' } from './componentEmits'
import { import {
EMPTY_OBJ, EMPTY_OBJ,
@ -131,7 +132,7 @@ export interface FunctionalComponent<
> extends ComponentInternalOptions { > extends ComponentInternalOptions {
// use of any here is intentional so it can be a valid JSX Element constructor // use of any here is intentional so it can be a valid JSX Element constructor
( (
props: P, props: P & EmitsToProps<E>,
ctx: Omit<SetupContext<E, IfAny<S, {}, SlotsType<S>>>, 'expose'> ctx: Omit<SetupContext<E, IfAny<S, {}, SlotsType<S>>>, 'expose'>
): any ): any
props?: ComponentPropsOptions<P> props?: ComponentPropsOptions<P>

View File

@ -504,7 +504,12 @@ function createSuspenseBoundary(
if (delayEnter) { if (delayEnter) {
activeBranch!.transition!.afterLeave = () => { activeBranch!.transition!.afterLeave = () => {
if (pendingId === suspense.pendingId) { if (pendingId === suspense.pendingId) {
move(pendingBranch!, container, anchor, MoveType.ENTER) move(
pendingBranch!,
container,
next(activeBranch!),
MoveType.ENTER
)
queuePostFlushCb(effects) queuePostFlushCb(effects)
} }
} }
@ -577,7 +582,6 @@ function createSuspenseBoundary(
// invoke @fallback event // invoke @fallback event
triggerEvent(vnode, 'onFallback') triggerEvent(vnode, 'onFallback')
const anchor = next(activeBranch!)
const mountFallback = () => { const mountFallback = () => {
if (!suspense.isInFallback) { if (!suspense.isInFallback) {
return return
@ -587,7 +591,7 @@ function createSuspenseBoundary(
null, null,
fallbackVNode, fallbackVNode,
container, container,
anchor, next(activeBranch!),
parentComponent, parentComponent,
null, // fallback tree will not have suspense context null, // fallback tree will not have suspense context
isSVG, isSVG,

View File

@ -11,9 +11,9 @@ export function initCustomFormatter() {
} }
const vueStyle = { style: 'color:#3ba776' } const vueStyle = { style: 'color:#3ba776' }
const numberStyle = { style: 'color:#0b1bc9' } const numberStyle = { style: 'color:#1677ff' }
const stringStyle = { style: 'color:#b62e24' } const stringStyle = { style: 'color:#f5222d' }
const keywordStyle = { style: 'color:#9d288c' } const keywordStyle = { style: 'color:#eb2f96' }
// custom formatter for Chrome // custom formatter for Chrome
// https://www.mattzeunert.com/2016/02/19/custom-chrome-devtools-object-formatters.html // https://www.mattzeunert.com/2016/02/19/custom-chrome-devtools-object-formatters.html

View File

@ -291,6 +291,15 @@ describe('runtime-dom: props patching', () => {
expect(el.value).toBe('baz') expect(el.value).toBe('baz')
}) })
// #8780
test('embedded tag with width and height', () => {
// Width and height of some embedded element such as img、video、source、canvas
// must be set as attribute
const el = document.createElement('img')
patchProp(el, 'width', null, '24px')
expect(el.getAttribute('width')).toBe('24px')
})
test('translate attribute', () => { test('translate attribute', () => {
const el = document.createElement('div') const el = document.createElement('div')
patchProp(el, 'translate', null, 'no') patchProp(el, 'translate', null, 'no')

View File

@ -32,14 +32,22 @@ const modifierGuards: Record<
/** /**
* @private * @private
*/ */
export const withModifiers = (fn: Function, modifiers: string[]) => { export const withModifiers = <
return (event: Event, ...args: unknown[]) => { T extends (event: Event, ...args: unknown[]) => any
for (let i = 0; i < modifiers.length; i++) { >(
const guard = modifierGuards[modifiers[i]] fn: T & { _withMods?: T },
if (guard && guard(event, modifiers)) return modifiers: string[]
} ) => {
return fn(event, ...args) return (
} fn._withMods ||
(fn._withMods = ((event, ...args) => {
for (let i = 0; i < modifiers.length; i++) {
const guard = modifierGuards[modifiers[i]]
if (guard && guard(event, modifiers)) return
}
return fn(event, ...args)
}) as T)
)
} }
// Kept for 2.x compat. // Kept for 2.x compat.
@ -57,7 +65,10 @@ const keyNames: Record<string, string | string[]> = {
/** /**
* @private * @private
*/ */
export const withKeys = (fn: Function, modifiers: string[]) => { export const withKeys = <T extends (event: KeyboardEvent) => any>(
fn: T & { _withKeys?: T },
modifiers: string[]
) => {
let globalKeyCodes: LegacyConfig['keyCodes'] let globalKeyCodes: LegacyConfig['keyCodes']
let instance: ComponentInternalInstance | null = null let instance: ComponentInternalInstance | null = null
if (__COMPAT__) { if (__COMPAT__) {
@ -77,40 +88,43 @@ export const withKeys = (fn: Function, modifiers: string[]) => {
} }
} }
return (event: KeyboardEvent) => { return (
if (!('key' in event)) { fn._withKeys ||
return (fn._withKeys = (event => {
} if (!('key' in event)) {
return
}
const eventKey = hyphenate(event.key) const eventKey = hyphenate(event.key)
if (modifiers.some(k => k === eventKey || keyNames[k] === eventKey)) { if (modifiers.some(k => k === eventKey || keyNames[k] === eventKey)) {
return fn(event)
}
if (__COMPAT__) {
const keyCode = String(event.keyCode)
if (
compatUtils.isCompatEnabled(
DeprecationTypes.V_ON_KEYCODE_MODIFIER,
instance
) &&
modifiers.some(mod => mod == keyCode)
) {
return fn(event) return fn(event)
} }
if (globalKeyCodes) {
for (const mod of modifiers) { if (__COMPAT__) {
const codes = globalKeyCodes[mod] const keyCode = String(event.keyCode)
if (codes) { if (
const matches = isArray(codes) compatUtils.isCompatEnabled(
? codes.some(code => String(code) === keyCode) DeprecationTypes.V_ON_KEYCODE_MODIFIER,
: String(codes) === keyCode instance
if (matches) { ) &&
return fn(event) modifiers.some(mod => mod == keyCode)
) {
return fn(event)
}
if (globalKeyCodes) {
for (const mod of modifiers) {
const codes = globalKeyCodes[mod]
if (codes) {
const matches = isArray(codes)
? codes.some(code => String(code) === keyCode)
: String(codes) === keyCode
if (matches) {
return fn(event)
}
} }
} }
} }
} }
} }) as T)
} )
} }

View File

@ -6,7 +6,12 @@ import { patchEvent } from './modules/events'
import { isOn, isString, isFunction, isModelListener } from '@vue/shared' import { isOn, isString, isFunction, isModelListener } from '@vue/shared'
import { RendererOptions } from '@vue/runtime-core' import { RendererOptions } from '@vue/runtime-core'
const nativeOnRE = /^on[a-z]/ const isNativeOn = (key: string) =>
key.charCodeAt(0) === 111 /* o */ &&
key.charCodeAt(1) === 110 /* n */ &&
// lowercase letter
key.charCodeAt(2) > 96 &&
key.charCodeAt(2) < 123
type DOMRendererOptions = RendererOptions<Node, Element> type DOMRendererOptions = RendererOptions<Node, Element>
@ -73,7 +78,7 @@ function shouldSetAsProp(
return true return true
} }
// or native onclick with function values // or native onclick with function values
if (key in el && nativeOnRE.test(key) && isFunction(value)) { if (key in el && isNativeOn(key) && isFunction(value)) {
return true return true
} }
return false return false
@ -105,8 +110,19 @@ function shouldSetAsProp(
return false return false
} }
// #8780 the width or heigth of embedded tags must be set as attribute
if (key === 'width' || key === 'height') {
const tag = el.tagName
return !(
tag === 'IMG' ||
tag === 'VIDEO' ||
tag === 'CANVAS' ||
tag === 'SOURCE'
)
}
// native onclick with string value, must be set as attribute // native onclick with string value, must be set as attribute
if (nativeOnRE.test(key) && isString(value)) { if (isNativeOn(key) && isString(value)) {
return false return false
} }

View File

@ -17,7 +17,10 @@ import {
renderSlot, renderSlot,
onErrorCaptured, onErrorCaptured,
onServerPrefetch, onServerPrefetch,
getCurrentInstance getCurrentInstance,
reactive,
computed,
createSSRApp
} from 'vue' } from 'vue'
import { escapeHtml } from '@vue/shared' import { escapeHtml } from '@vue/shared'
import { renderToString } from '../src/renderToString' import { renderToString } from '../src/renderToString'
@ -1140,5 +1143,47 @@ function testRender(type: string, render: typeof renderToString) {
expect(renderError).toBe(null) expect(renderError).toBe(null)
expect((capturedError as unknown as Error).message).toBe('An error') expect((capturedError as unknown as Error).message).toBe('An error')
}) })
test('computed reactivity during SSR with onServerPrefetch', async () => {
const store = {
// initial state could be hydrated
state: reactive({ items: null as null | string[] }),
// pretend to fetch some data from an api
async fetchData() {
this.state.items = ['hello', 'world']
}
}
const getterSpy = vi.fn()
const App = defineComponent(() => {
const msg = computed(() => {
getterSpy()
return store.state.items?.join(' ')
})
// If msg value is falsy then we are either in ssr context or on the client
// and the initial state was not modified/hydrated.
// In both cases we need to fetch data.
onServerPrefetch(() => store.fetchData())
// simulate the read from a composable (e.g. filtering a list of results)
msg.value
return () => h('div', null, msg.value)
})
const app = createSSRApp(App)
// in real world serve this html and append store state for hydration on client
const html = await renderToString(app)
expect(html).toMatch('hello world')
// should only be called twice since access should be cached
// during the render phase
expect(getterSpy).toHaveBeenCalledTimes(2)
})
}) })
} }

View File

@ -33,7 +33,6 @@ test('computed reactivity during SSR', async () => {
// In both cases we need to fetch data. // In both cases we need to fetch data.
if (!msg.value) await store.fetchData() if (!msg.value) await store.fetchData()
expect(msg.value).toBe('hello world')
return () => h('div', null, msg.value + msg.value + msg.value) return () => h('div', null, msg.value + msg.value + msg.value)
}) })

View File

@ -144,7 +144,10 @@ function renderComponentSubTree(
// perf: enable caching of computed getters during render // perf: enable caching of computed getters during render
// since there cannot be state mutations during render. // since there cannot be state mutations during render.
for (const e of instance.scope.effects) { for (const e of instance.scope.effects) {
if (e.computed) e.computed._cacheable = true if (e.computed) {
e.computed._dirty = true
e.computed._cacheable = true
}
} }
const ssrRender = instance.ssrRender || comp.ssrRender const ssrRender = instance.ssrRender || comp.ssrRender

View File

@ -13,7 +13,7 @@
"vite": "^5.0.0" "vite": "^5.0.0"
}, },
"dependencies": { "dependencies": {
"@vue/repl": "^2.7.0", "@vue/repl": "^3.0.0",
"file-saver": "^2.0.5", "file-saver": "^2.0.5",
"jszip": "^3.10.1", "jszip": "^3.10.1",
"vue": "workspace:*" "vue": "workspace:*"

View File

@ -1,8 +1,22 @@
<script setup lang="ts"> <script setup lang="ts">
import Header from './Header.vue' import Header from './Header.vue'
import { Repl, ReplStore, SFCOptions } from '@vue/repl' import { Repl, ReplStore, SFCOptions } from '@vue/repl'
import Monaco from '@vue/repl/monaco-editor' import type Monaco from '@vue/repl/monaco-editor'
import type CodeMirror from '@vue/repl/codemirror-editor'
import { ref, watchEffect, onMounted } from 'vue' import { ref, watchEffect, onMounted } from 'vue'
import { shallowRef } from 'vue'
const EditorComponent = shallowRef<typeof Monaco | typeof CodeMirror>()
if (import.meta.env.DEV) {
import('@vue/repl/codemirror-editor').then(
mod => (EditorComponent.value = mod.default)
)
} else {
import('@vue/repl/monaco-editor').then(
mod => (EditorComponent.value = mod.default)
)
}
const setVH = () => { const setVH = () => {
document.documentElement.style.setProperty('--vh', window.innerHeight + `px`) document.documentElement.style.setProperty('--vh', window.innerHeight + `px`)
@ -97,8 +111,9 @@ onMounted(() => {
@toggle-ssr="toggleSSR" @toggle-ssr="toggleSSR"
/> />
<Repl <Repl
v-if="EditorComponent"
:theme="theme" :theme="theme"
:editor="Monaco" :editor="EditorComponent"
@keydown.ctrl.s.prevent @keydown.ctrl.s.prevent
@keydown.meta.s.prevent @keydown.meta.s.prevent
:ssr="useSSRMode" :ssr="useSSRMode"

View File

@ -1,6 +1,5 @@
import { createApp } from 'vue' import { createApp } from 'vue'
import App from './App.vue' import App from './App.vue'
import '@vue/repl/style.css'
// @ts-expect-error Custom window property // @ts-expect-error Custom window property
window.VUE_DEVTOOLS_CONFIG = { window.VUE_DEVTOOLS_CONFIG = {

View File

@ -12,8 +12,11 @@ export const NOOP = () => {}
*/ */
export const NO = () => false export const NO = () => false
const onRE = /^on[^a-z]/ export const isOn = (key: string) =>
export const isOn = (key: string) => onRE.test(key) key.charCodeAt(0) === 111 /* o */ &&
key.charCodeAt(1) === 110 /* n */ &&
// uppercase letter
(key.charCodeAt(2) > 122 || key.charCodeAt(2) < 97)
export const isModelListener = (key: string) => key.startsWith('onUpdate:') export const isModelListener = (key: string) => key.startsWith('onUpdate:')

View File

@ -1,5 +1,6 @@
body { body {
margin: 0; margin: 0;
overflow: hidden;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
--bg: #1D1F21; --bg: #1D1F21;
--border: #333; --border: #333;

View File

@ -38,7 +38,7 @@
}, },
"homepage": "https://github.com/vuejs/core/tree/main/packages/vue-compat#readme", "homepage": "https://github.com/vuejs/core/tree/main/packages/vue-compat#readme",
"dependencies": { "dependencies": {
"@babel/parser": "^7.23.4", "@babel/parser": "^7.23.5",
"estree-walker": "^2.0.2", "estree-walker": "^2.0.2",
"source-map-js": "^1.0.2" "source-map-js": "^1.0.2"
}, },

View File

@ -1586,6 +1586,72 @@ describe('e2e: Transition', () => {
expect(barMountSpy).toBeCalledTimes(1) expect(barMountSpy).toBeCalledTimes(1)
expect(barMountSpy).toHaveBeenNthCalledWith(1, true, false, true) expect(barMountSpy).toHaveBeenNthCalledWith(1, true, false, true)
}) })
// #8105
test(
'trigger again when transition is not finished',
async () => {
await page().evaluate(duration => {
const { createApp, shallowRef, h } = (window as any).Vue
const One = {
async setup() {
return () => h('div', { class: 'test' }, 'one')
}
}
const Two = {
async setup() {
return () => h('div', { class: 'test' }, 'two')
}
}
createApp({
template: `
<div id="container">
<transition name="test" mode="out-in" duration="${duration}">
<Suspense>
<component :is="view"/>
</Suspense>
</transition>
</div>
<button id="toggleBtn" @click="click">button</button>
`,
setup: () => {
const view = shallowRef(One)
const click = () => {
view.value = view.value === One ? Two : One
}
return { view, click }
}
}).mount('#app')
}, duration)
await nextFrame()
expect(await html('#container')).toBe(
'<div class="test test-enter-active test-enter-to">one</div>'
)
await transitionFinish()
expect(await html('#container')).toBe('<div class="test">one</div>')
// trigger twice
classWhenTransitionStart()
classWhenTransitionStart()
await nextFrame()
expect(await html('#container')).toBe(
'<div class="test test-leave-active test-leave-to">one</div>'
)
await transitionFinish()
await nextFrame()
expect(await html('#container')).toBe(
'<div class="test test-enter-active test-enter-to">one</div>'
)
await transitionFinish()
await nextFrame()
expect(await html('#container')).toBe('<div class="test">one</div>')
},
E2E_TIMEOUT
)
}) })
describe('transition with v-show', () => { describe('transition with v-show', () => {

View File

@ -1,5 +1,3 @@
if (typeof require !== 'undefined') { if (typeof require !== 'undefined') {
try { require('@vue/compiler-sfc').registerTS(() => require('typescript'))
require('@vue/compiler-sfc').registerTS(() => require('typescript'))
} catch (e) {}
} }

View File

@ -4,14 +4,32 @@ import { initDev } from './dev'
import { compile, CompilerOptions, CompilerError } from '@vue/compiler-dom' import { compile, CompilerOptions, CompilerError } from '@vue/compiler-dom'
import { registerRuntimeCompiler, RenderFunction, warn } from '@vue/runtime-dom' import { registerRuntimeCompiler, RenderFunction, warn } from '@vue/runtime-dom'
import * as runtimeDom from '@vue/runtime-dom' import * as runtimeDom from '@vue/runtime-dom'
import { isString, NOOP, generateCodeFrame, extend } from '@vue/shared' import {
isString,
NOOP,
generateCodeFrame,
extend,
EMPTY_OBJ
} from '@vue/shared'
import { InternalRenderFunction } from 'packages/runtime-core/src/component' import { InternalRenderFunction } from 'packages/runtime-core/src/component'
if (__DEV__) { if (__DEV__) {
initDev() initDev()
} }
const compileCache: Record<string, RenderFunction> = Object.create(null) const compileCache = new WeakMap<
CompilerOptions,
Record<string, RenderFunction>
>()
function getCache(options?: CompilerOptions) {
let c = compileCache.get(options ?? EMPTY_OBJ)
if (!c) {
c = Object.create(null) as Record<string, RenderFunction>
compileCache.set(options ?? EMPTY_OBJ, c)
}
return c
}
function compileToFunction( function compileToFunction(
template: string | HTMLElement, template: string | HTMLElement,
@ -27,7 +45,8 @@ function compileToFunction(
} }
const key = template const key = template
const cached = compileCache[key] const cache = getCache(options)
const cached = cache[key]
if (cached) { if (cached) {
return cached return cached
} }
@ -84,7 +103,7 @@ function compileToFunction(
// mark the function as runtime compiled // mark the function as runtime compiled
;(render as InternalRenderFunction)._rc = true ;(render as InternalRenderFunction)._rc = true
return (compileCache[key] = render) return (cache[key] = render)
} }
registerRuntimeCompiler(compileToFunction) registerRuntimeCompiler(compileToFunction)

View File

@ -9,11 +9,11 @@ importers:
.: .:
devDependencies: devDependencies:
'@babel/parser': '@babel/parser':
specifier: ^7.23.4 specifier: ^7.23.5
version: 7.23.4 version: 7.23.5
'@babel/types': '@babel/types':
specifier: ^7.23.4 specifier: ^7.23.5
version: 7.23.4 version: 7.23.5
'@rollup/plugin-alias': '@rollup/plugin-alias':
specifier: ^5.0.1 specifier: ^5.0.1
version: 5.0.1(rollup@4.1.4) version: 5.0.1(rollup@4.1.4)
@ -39,8 +39,8 @@ importers:
specifier: ^1.2.5 specifier: ^1.2.5
version: 1.2.5 version: 1.2.5
'@types/node': '@types/node':
specifier: ^20.10.0 specifier: ^20.10.3
version: 20.10.0 version: 20.10.3
'@types/semver': '@types/semver':
specifier: ^7.5.5 specifier: ^7.5.5
version: 7.5.5 version: 7.5.5
@ -117,8 +117,8 @@ importers:
specifier: ^3.0.2 specifier: ^3.0.2
version: 3.0.2 version: 3.0.2
puppeteer: puppeteer:
specifier: ~21.5.1 specifier: ~21.5.2
version: 21.5.1(typescript@5.2.2) version: 21.5.2(typescript@5.2.2)
rimraf: rimraf:
specifier: ^5.0.5 specifier: ^5.0.5
version: 5.0.5 version: 5.0.5
@ -153,14 +153,14 @@ importers:
specifier: ^2.6.2 specifier: ^2.6.2
version: 2.6.2 version: 2.6.2
tsx: tsx:
specifier: ^4.5.0 specifier: ^4.6.2
version: 4.5.0 version: 4.6.2
typescript: typescript:
specifier: ^5.2.2 specifier: ^5.2.2
version: 5.2.2 version: 5.2.2
vite: vite:
specifier: ^5.0.0 specifier: ^5.0.0
version: 5.0.0(@types/node@20.10.0)(terser@5.22.0) version: 5.0.0(@types/node@20.10.3)(terser@5.22.0)
vitest: vitest:
specifier: ^0.34.6 specifier: ^0.34.6
version: 0.34.6(jsdom@22.1.0)(terser@5.22.0) version: 0.34.6(jsdom@22.1.0)(terser@5.22.0)
@ -168,8 +168,8 @@ importers:
packages/compiler-core: packages/compiler-core:
dependencies: dependencies:
'@babel/parser': '@babel/parser':
specifier: ^7.23.4 specifier: ^7.23.5
version: 7.23.4 version: 7.23.5
'@vue/shared': '@vue/shared':
specifier: workspace:* specifier: workspace:*
version: link:../shared version: link:../shared
@ -184,8 +184,8 @@ importers:
version: 1.0.2 version: 1.0.2
devDependencies: devDependencies:
'@babel/types': '@babel/types':
specifier: ^7.23.4 specifier: ^7.23.5
version: 7.23.4 version: 7.23.5
packages/compiler-dom: packages/compiler-dom:
dependencies: dependencies:
@ -199,8 +199,8 @@ importers:
packages/compiler-sfc: packages/compiler-sfc:
dependencies: dependencies:
'@babel/parser': '@babel/parser':
specifier: ^7.23.4 specifier: ^7.23.5
version: 7.23.4 version: 7.23.5
'@vue/compiler-core': '@vue/compiler-core':
specifier: workspace:* specifier: workspace:*
version: link:../compiler-core version: link:../compiler-core
@ -220,15 +220,15 @@ importers:
specifier: ^0.30.5 specifier: ^0.30.5
version: 0.30.5 version: 0.30.5
postcss: postcss:
specifier: ^8.4.31 specifier: ^8.4.32
version: 8.4.31 version: 8.4.32
source-map-js: source-map-js:
specifier: ^1.0.2 specifier: ^1.0.2
version: 1.0.2 version: 1.0.2
devDependencies: devDependencies:
'@babel/types': '@babel/types':
specifier: ^7.23.4 specifier: ^7.23.5
version: 7.23.4 version: 7.23.5
'@vue/consolidate': '@vue/consolidate':
specifier: ^0.17.3 specifier: ^0.17.3
version: 0.17.3 version: 0.17.3
@ -246,7 +246,7 @@ importers:
version: 9.0.3 version: 9.0.3
postcss-modules: postcss-modules:
specifier: ^6.0.0 specifier: ^6.0.0
version: 6.0.0(postcss@8.4.31) version: 6.0.0(postcss@8.4.32)
postcss-selector-parser: postcss-selector-parser:
specifier: ^6.0.13 specifier: ^6.0.13
version: 6.0.13 version: 6.0.13
@ -338,8 +338,8 @@ importers:
packages/sfc-playground: packages/sfc-playground:
dependencies: dependencies:
'@vue/repl': '@vue/repl':
specifier: ^2.7.0 specifier: ^3.0.0
version: 2.7.0 version: 3.0.0
file-saver: file-saver:
specifier: ^2.0.5 specifier: ^2.0.5
version: 2.0.5 version: 2.0.5
@ -355,7 +355,7 @@ importers:
version: 4.4.0(vite@5.0.0)(vue@packages+vue) version: 4.4.0(vite@5.0.0)(vue@packages+vue)
vite: vite:
specifier: ^5.0.0 specifier: ^5.0.0
version: 5.0.0(@types/node@20.10.0)(terser@5.22.0) version: 5.0.0(@types/node@20.10.3)(terser@5.22.0)
packages/shared: {} packages/shared: {}
@ -392,8 +392,8 @@ importers:
packages/vue-compat: packages/vue-compat:
dependencies: dependencies:
'@babel/parser': '@babel/parser':
specifier: ^7.23.4 specifier: ^7.23.5
version: 7.23.4 version: 7.23.5
estree-walker: estree-walker:
specifier: ^2.0.2 specifier: ^2.0.2
version: 2.0.2 version: 2.0.2
@ -428,25 +428,33 @@ packages:
chalk: 2.4.2 chalk: 2.4.2
dev: true dev: true
/@babel/code-frame@7.23.5:
resolution: {integrity: sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==}
engines: {node: '>=6.9.0'}
dependencies:
'@babel/highlight': 7.23.4
chalk: 2.4.2
dev: true
/@babel/compat-data@7.23.2: /@babel/compat-data@7.23.2:
resolution: {integrity: sha512-0S9TQMmDHlqAZ2ITT95irXKfxN9bncq8ZCoJhun3nHL/lLUxd2NKBJYoNGWH7S0hz6fRQwWlAWn/ILM0C70KZQ==} resolution: {integrity: sha512-0S9TQMmDHlqAZ2ITT95irXKfxN9bncq8ZCoJhun3nHL/lLUxd2NKBJYoNGWH7S0hz6fRQwWlAWn/ILM0C70KZQ==}
engines: {node: '>=6.9.0'} engines: {node: '>=6.9.0'}
dev: true dev: true
/@babel/core@7.23.3: /@babel/core@7.23.5:
resolution: {integrity: sha512-Jg+msLuNuCJDyBvFv5+OKOUjWMZgd85bKjbICd3zWrKAo+bJ49HJufi7CQE0q0uR8NGyO6xkCACScNqyjHSZew==} resolution: {integrity: sha512-Cwc2XjUrG4ilcfOw4wBAK+enbdgwAcAJCfGUItPBKR7Mjw4aEfAFYrLxeRp4jWgtNIKn3n2AlBOfwwafl+42/g==}
engines: {node: '>=6.9.0'} engines: {node: '>=6.9.0'}
dependencies: dependencies:
'@ampproject/remapping': 2.2.1 '@ampproject/remapping': 2.2.1
'@babel/code-frame': 7.22.13 '@babel/code-frame': 7.23.5
'@babel/generator': 7.23.3 '@babel/generator': 7.23.5
'@babel/helper-compilation-targets': 7.22.15 '@babel/helper-compilation-targets': 7.22.15
'@babel/helper-module-transforms': 7.23.3(@babel/core@7.23.3) '@babel/helper-module-transforms': 7.23.3(@babel/core@7.23.5)
'@babel/helpers': 7.23.2 '@babel/helpers': 7.23.5
'@babel/parser': 7.23.4 '@babel/parser': 7.23.5
'@babel/template': 7.22.15 '@babel/template': 7.22.15
'@babel/traverse': 7.23.3 '@babel/traverse': 7.23.5
'@babel/types': 7.23.4 '@babel/types': 7.23.5
convert-source-map: 2.0.0 convert-source-map: 2.0.0
debug: 4.3.4 debug: 4.3.4
gensync: 1.0.0-beta.2 gensync: 1.0.0-beta.2
@ -456,11 +464,11 @@ packages:
- supports-color - supports-color
dev: true dev: true
/@babel/generator@7.23.3: /@babel/generator@7.23.5:
resolution: {integrity: sha512-keeZWAV4LU3tW0qRi19HRpabC/ilM0HRBBzf9/k8FFiG4KVpiv0FIy4hHfLfFQZNhziCTPTmd59zoyv6DNISzg==} resolution: {integrity: sha512-BPssCHrBD+0YrxviOa3QzpqwhNIXKEtOa2jQrm4FlmkC2apYgRnQcmPWiGZDlGxiNtltnUFolMe8497Esry+jA==}
engines: {node: '>=6.9.0'} engines: {node: '>=6.9.0'}
dependencies: dependencies:
'@babel/types': 7.23.4 '@babel/types': 7.23.5
'@jridgewell/gen-mapping': 0.3.3 '@jridgewell/gen-mapping': 0.3.3
'@jridgewell/trace-mapping': 0.3.20 '@jridgewell/trace-mapping': 0.3.20
jsesc: 2.5.2 jsesc: 2.5.2
@ -487,30 +495,30 @@ packages:
engines: {node: '>=6.9.0'} engines: {node: '>=6.9.0'}
dependencies: dependencies:
'@babel/template': 7.22.15 '@babel/template': 7.22.15
'@babel/types': 7.23.4 '@babel/types': 7.23.5
dev: true dev: true
/@babel/helper-hoist-variables@7.22.5: /@babel/helper-hoist-variables@7.22.5:
resolution: {integrity: sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==} resolution: {integrity: sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==}
engines: {node: '>=6.9.0'} engines: {node: '>=6.9.0'}
dependencies: dependencies:
'@babel/types': 7.23.4 '@babel/types': 7.23.5
dev: true dev: true
/@babel/helper-module-imports@7.22.15: /@babel/helper-module-imports@7.22.15:
resolution: {integrity: sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==} resolution: {integrity: sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==}
engines: {node: '>=6.9.0'} engines: {node: '>=6.9.0'}
dependencies: dependencies:
'@babel/types': 7.23.4 '@babel/types': 7.23.5
dev: true dev: true
/@babel/helper-module-transforms@7.23.3(@babel/core@7.23.3): /@babel/helper-module-transforms@7.23.3(@babel/core@7.23.5):
resolution: {integrity: sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==} resolution: {integrity: sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==}
engines: {node: '>=6.9.0'} engines: {node: '>=6.9.0'}
peerDependencies: peerDependencies:
'@babel/core': ^7.0.0 '@babel/core': ^7.0.0
dependencies: dependencies:
'@babel/core': 7.23.3 '@babel/core': 7.23.5
'@babel/helper-environment-visitor': 7.22.20 '@babel/helper-environment-visitor': 7.22.20
'@babel/helper-module-imports': 7.22.15 '@babel/helper-module-imports': 7.22.15
'@babel/helper-simple-access': 7.22.5 '@babel/helper-simple-access': 7.22.5
@ -522,14 +530,14 @@ packages:
resolution: {integrity: sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==} resolution: {integrity: sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==}
engines: {node: '>=6.9.0'} engines: {node: '>=6.9.0'}
dependencies: dependencies:
'@babel/types': 7.23.4 '@babel/types': 7.23.5
dev: true dev: true
/@babel/helper-split-export-declaration@7.22.6: /@babel/helper-split-export-declaration@7.22.6:
resolution: {integrity: sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==} resolution: {integrity: sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==}
engines: {node: '>=6.9.0'} engines: {node: '>=6.9.0'}
dependencies: dependencies:
'@babel/types': 7.23.4 '@babel/types': 7.23.5
dev: true dev: true
/@babel/helper-string-parser@7.23.4: /@babel/helper-string-parser@7.23.4:
@ -546,13 +554,13 @@ packages:
engines: {node: '>=6.9.0'} engines: {node: '>=6.9.0'}
dev: true dev: true
/@babel/helpers@7.23.2: /@babel/helpers@7.23.5:
resolution: {integrity: sha512-lzchcp8SjTSVe/fPmLwtWVBFC7+Tbn8LGHDVfDp9JGxpAY5opSaEFgt8UQvrnECWOTdji2mOWMz1rOhkHscmGQ==} resolution: {integrity: sha512-oO7us8FzTEsG3U6ag9MfdF1iA/7Z6dz+MtFhifZk8C8o453rGJFFWUP1t+ULM9TUIAzC9uxXEiXjOiVMyd7QPg==}
engines: {node: '>=6.9.0'} engines: {node: '>=6.9.0'}
dependencies: dependencies:
'@babel/template': 7.22.15 '@babel/template': 7.22.15
'@babel/traverse': 7.23.3 '@babel/traverse': 7.23.5
'@babel/types': 7.23.4 '@babel/types': 7.23.5
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
dev: true dev: true
@ -567,42 +575,51 @@ packages:
js-tokens: 4.0.0 js-tokens: 4.0.0
dev: true dev: true
/@babel/parser@7.23.4: /@babel/highlight@7.23.4:
resolution: {integrity: sha512-vf3Xna6UEprW+7t6EtOmFpHNAuxw3xqPZghy+brsnusscJRW5BMUzzHZc5ICjULee81WeUV2jjakG09MDglJXQ==} resolution: {integrity: sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==}
engines: {node: '>=6.9.0'}
dependencies:
'@babel/helper-validator-identifier': 7.22.20
chalk: 2.4.2
js-tokens: 4.0.0
dev: true
/@babel/parser@7.23.5:
resolution: {integrity: sha512-hOOqoiNXrmGdFbhgCzu6GiURxUgM27Xwd/aPuu8RfHEZPBzL1Z54okAHAQjXfcQNwvrlkAmAp4SlRTZ45vlthQ==}
engines: {node: '>=6.0.0'} engines: {node: '>=6.0.0'}
hasBin: true hasBin: true
dependencies: dependencies:
'@babel/types': 7.23.4 '@babel/types': 7.23.5
/@babel/template@7.22.15: /@babel/template@7.22.15:
resolution: {integrity: sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==} resolution: {integrity: sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==}
engines: {node: '>=6.9.0'} engines: {node: '>=6.9.0'}
dependencies: dependencies:
'@babel/code-frame': 7.22.13 '@babel/code-frame': 7.23.5
'@babel/parser': 7.23.4 '@babel/parser': 7.23.5
'@babel/types': 7.23.4 '@babel/types': 7.23.5
dev: true dev: true
/@babel/traverse@7.23.3: /@babel/traverse@7.23.5:
resolution: {integrity: sha512-+K0yF1/9yR0oHdE0StHuEj3uTPzwwbrLGfNOndVJVV2TqA5+j3oljJUb4nmB954FLGjNem976+B+eDuLIjesiQ==} resolution: {integrity: sha512-czx7Xy5a6sapWWRx61m1Ke1Ra4vczu1mCTtJam5zRTBOonfdJ+S/B6HYmGYu3fJtr8GGET3si6IhgWVBhJ/m8w==}
engines: {node: '>=6.9.0'} engines: {node: '>=6.9.0'}
dependencies: dependencies:
'@babel/code-frame': 7.22.13 '@babel/code-frame': 7.23.5
'@babel/generator': 7.23.3 '@babel/generator': 7.23.5
'@babel/helper-environment-visitor': 7.22.20 '@babel/helper-environment-visitor': 7.22.20
'@babel/helper-function-name': 7.23.0 '@babel/helper-function-name': 7.23.0
'@babel/helper-hoist-variables': 7.22.5 '@babel/helper-hoist-variables': 7.22.5
'@babel/helper-split-export-declaration': 7.22.6 '@babel/helper-split-export-declaration': 7.22.6
'@babel/parser': 7.23.4 '@babel/parser': 7.23.5
'@babel/types': 7.23.4 '@babel/types': 7.23.5
debug: 4.3.4 debug: 4.3.4
globals: 11.12.0 globals: 11.12.0
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
dev: true dev: true
/@babel/types@7.23.4: /@babel/types@7.23.5:
resolution: {integrity: sha512-7uIFwVYpoplT5jp/kVv6EF93VaJ8H+Yn5IczYiaAi98ajzjfoZfslet/e0sLh+wVBjb2qqIut1b0S26VSafsSQ==} resolution: {integrity: sha512-ON5kSOJwVO6xXVRTvOI0eOnWe7VdUcIpsovGo9U/Br4Ie4UVFQTboO2cYnDhAGU6Fp+UxSiT+pMft0SMHfuq6w==}
engines: {node: '>=6.9.0'} engines: {node: '>=6.9.0'}
dependencies: dependencies:
'@babel/helper-string-parser': 7.23.4 '@babel/helper-string-parser': 7.23.4
@ -1527,8 +1544,8 @@ packages:
resolution: {integrity: sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag==} resolution: {integrity: sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag==}
dev: true dev: true
/@types/node@20.10.0: /@types/node@20.10.3:
resolution: {integrity: sha512-D0WfRmU9TQ8I9PFx9Yc+EBHw+vSpIub4IDvQivcp26PtPrdMGAq5SDcpXEo/epqa/DXotVpekHiLNTg3iaKXBQ==} resolution: {integrity: sha512-XJavIpZqiXID5Yxnxv3RUDKTN5b81ddNC3ecsA0SoFXz/QU8OGBwZGMomiq0zw+uuqbL/krztv/DINAQ/EV4gg==}
dependencies: dependencies:
undici-types: 5.26.5 undici-types: 5.26.5
dev: true dev: true
@ -1549,7 +1566,7 @@ packages:
resolution: {integrity: sha512-Km7XAtUIduROw7QPgvcft0lIupeG8a8rdKL8RiSyKvlE7dYY31fEn41HVuQsRFDuROA8tA4K2UVL+WdfFmErBA==} resolution: {integrity: sha512-Km7XAtUIduROw7QPgvcft0lIupeG8a8rdKL8RiSyKvlE7dYY31fEn41HVuQsRFDuROA8tA4K2UVL+WdfFmErBA==}
requiresBuild: true requiresBuild: true
dependencies: dependencies:
'@types/node': 20.10.0 '@types/node': 20.10.3
dev: true dev: true
optional: true optional: true
@ -1689,7 +1706,7 @@ packages:
vite: ^4.0.0 vite: ^4.0.0
vue: ^3.2.25 vue: ^3.2.25
dependencies: dependencies:
vite: 5.0.0(@types/node@20.10.0)(terser@5.22.0) vite: 5.0.0(@types/node@20.10.3)(terser@5.22.0)
vue: link:packages/vue vue: link:packages/vue
dev: true dev: true
@ -1753,8 +1770,8 @@ packages:
engines: {node: '>= 0.12.0'} engines: {node: '>= 0.12.0'}
dev: true dev: true
/@vue/repl@2.7.0: /@vue/repl@3.0.0:
resolution: {integrity: sha512-zzyb+tVvzmOePv8Gp4sefP/7CKidx4WiJDfKPP698b9bN5jSFtmSOg4nvPoJEE1ICKeAEgdRKVneYJ8Mp7C/WA==} resolution: {integrity: sha512-tGYibiftMo5yEuIKPWVsNuuNDejjJk0JQmvKtTm12KNLFqtGD7fWoGv1qUzcN9EAxwVeDgnT9ljRgqGVgZkyEg==}
dev: false dev: false
/@zeit/schemas@2.29.0: /@zeit/schemas@2.29.0:
@ -1983,7 +2000,7 @@ packages:
resolution: {integrity: sha512-GAwkz0AihzY5bkwIY5QDR+LvsRQgB/B+1foMPvi0FZPMl5fjD7ICiznUiBdLYMH1QYe6vqu4gWYytZOccLouFw==} resolution: {integrity: sha512-GAwkz0AihzY5bkwIY5QDR+LvsRQgB/B+1foMPvi0FZPMl5fjD7ICiznUiBdLYMH1QYe6vqu4gWYytZOccLouFw==}
engines: {node: '>= 10.0.0'} engines: {node: '>= 10.0.0'}
dependencies: dependencies:
'@babel/types': 7.23.4 '@babel/types': 7.23.5
dev: true dev: true
/balanced-match@1.0.2: /balanced-match@1.0.2:
@ -2304,8 +2321,8 @@ packages:
/constantinople@4.0.1: /constantinople@4.0.1:
resolution: {integrity: sha512-vCrqcSIq4//Gx74TXXCGnHpulY1dskqLTFGDmhrGxzeXL8lF8kvXv6mpNWlJj1uD4DW23D4ljAqbY4RRaaUZIw==} resolution: {integrity: sha512-vCrqcSIq4//Gx74TXXCGnHpulY1dskqLTFGDmhrGxzeXL8lF8kvXv6mpNWlJj1uD4DW23D4ljAqbY4RRaaUZIw==}
dependencies: dependencies:
'@babel/parser': 7.23.4 '@babel/parser': 7.23.5
'@babel/types': 7.23.4 '@babel/types': 7.23.5
dev: true dev: true
/content-disposition@0.5.2: /content-disposition@0.5.2:
@ -3520,13 +3537,13 @@ packages:
safer-buffer: 2.1.2 safer-buffer: 2.1.2
dev: true dev: true
/icss-utils@5.1.0(postcss@8.4.31): /icss-utils@5.1.0(postcss@8.4.32):
resolution: {integrity: sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==} resolution: {integrity: sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==}
engines: {node: ^10 || ^12 || >= 14} engines: {node: ^10 || ^12 || >= 14}
peerDependencies: peerDependencies:
postcss: ^8.1.0 postcss: ^8.1.0
dependencies: dependencies:
postcss: 8.4.31 postcss: 8.4.32
dev: true dev: true
/ieee754@1.2.1: /ieee754@1.2.1:
@ -3823,8 +3840,8 @@ packages:
resolution: {integrity: sha512-EAMEJBsYuyyztxMxW3g7ugGPkrZsV57v0Hmv3mm1uQsmB+QnZuepg731CRaIgeUVSdmsTngOkSnauNF8p7FIhA==} resolution: {integrity: sha512-EAMEJBsYuyyztxMxW3g7ugGPkrZsV57v0Hmv3mm1uQsmB+QnZuepg731CRaIgeUVSdmsTngOkSnauNF8p7FIhA==}
engines: {node: '>=10'} engines: {node: '>=10'}
dependencies: dependencies:
'@babel/core': 7.23.3 '@babel/core': 7.23.5
'@babel/parser': 7.23.4 '@babel/parser': 7.23.5
'@istanbuljs/schema': 0.1.3 '@istanbuljs/schema': 0.1.3
istanbul-lib-coverage: 3.2.0 istanbul-lib-coverage: 3.2.0
semver: 7.5.4 semver: 7.5.4
@ -4298,8 +4315,8 @@ packages:
resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==}
dev: true dev: true
/nanoid@3.3.6: /nanoid@3.3.7:
resolution: {integrity: sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==} resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==}
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
hasBin: true hasBin: true
@ -4666,60 +4683,60 @@ packages:
pathe: 1.1.1 pathe: 1.1.1
dev: true dev: true
/postcss-modules-extract-imports@3.0.0(postcss@8.4.31): /postcss-modules-extract-imports@3.0.0(postcss@8.4.32):
resolution: {integrity: sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==} resolution: {integrity: sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==}
engines: {node: ^10 || ^12 || >= 14} engines: {node: ^10 || ^12 || >= 14}
peerDependencies: peerDependencies:
postcss: ^8.1.0 postcss: ^8.1.0
dependencies: dependencies:
postcss: 8.4.31 postcss: 8.4.32
dev: true dev: true
/postcss-modules-local-by-default@4.0.3(postcss@8.4.31): /postcss-modules-local-by-default@4.0.3(postcss@8.4.32):
resolution: {integrity: sha512-2/u2zraspoACtrbFRnTijMiQtb4GW4BvatjaG/bCjYQo8kLTdevCUlwuBHx2sCnSyrI3x3qj4ZK1j5LQBgzmwA==} resolution: {integrity: sha512-2/u2zraspoACtrbFRnTijMiQtb4GW4BvatjaG/bCjYQo8kLTdevCUlwuBHx2sCnSyrI3x3qj4ZK1j5LQBgzmwA==}
engines: {node: ^10 || ^12 || >= 14} engines: {node: ^10 || ^12 || >= 14}
peerDependencies: peerDependencies:
postcss: ^8.1.0 postcss: ^8.1.0
dependencies: dependencies:
icss-utils: 5.1.0(postcss@8.4.31) icss-utils: 5.1.0(postcss@8.4.32)
postcss: 8.4.31 postcss: 8.4.32
postcss-selector-parser: 6.0.13 postcss-selector-parser: 6.0.13
postcss-value-parser: 4.2.0 postcss-value-parser: 4.2.0
dev: true dev: true
/postcss-modules-scope@3.0.0(postcss@8.4.31): /postcss-modules-scope@3.0.0(postcss@8.4.32):
resolution: {integrity: sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg==} resolution: {integrity: sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg==}
engines: {node: ^10 || ^12 || >= 14} engines: {node: ^10 || ^12 || >= 14}
peerDependencies: peerDependencies:
postcss: ^8.1.0 postcss: ^8.1.0
dependencies: dependencies:
postcss: 8.4.31 postcss: 8.4.32
postcss-selector-parser: 6.0.13 postcss-selector-parser: 6.0.13
dev: true dev: true
/postcss-modules-values@4.0.0(postcss@8.4.31): /postcss-modules-values@4.0.0(postcss@8.4.32):
resolution: {integrity: sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==} resolution: {integrity: sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==}
engines: {node: ^10 || ^12 || >= 14} engines: {node: ^10 || ^12 || >= 14}
peerDependencies: peerDependencies:
postcss: ^8.1.0 postcss: ^8.1.0
dependencies: dependencies:
icss-utils: 5.1.0(postcss@8.4.31) icss-utils: 5.1.0(postcss@8.4.32)
postcss: 8.4.31 postcss: 8.4.32
dev: true dev: true
/postcss-modules@6.0.0(postcss@8.4.31): /postcss-modules@6.0.0(postcss@8.4.32):
resolution: {integrity: sha512-7DGfnlyi/ju82BRzTIjWS5C4Tafmzl3R79YP/PASiocj+aa6yYphHhhKUOEoXQToId5rgyFgJ88+ccOUydjBXQ==} resolution: {integrity: sha512-7DGfnlyi/ju82BRzTIjWS5C4Tafmzl3R79YP/PASiocj+aa6yYphHhhKUOEoXQToId5rgyFgJ88+ccOUydjBXQ==}
peerDependencies: peerDependencies:
postcss: ^8.0.0 postcss: ^8.0.0
dependencies: dependencies:
generic-names: 4.0.0 generic-names: 4.0.0
icss-utils: 5.1.0(postcss@8.4.31) icss-utils: 5.1.0(postcss@8.4.32)
lodash.camelcase: 4.3.0 lodash.camelcase: 4.3.0
postcss: 8.4.31 postcss: 8.4.32
postcss-modules-extract-imports: 3.0.0(postcss@8.4.31) postcss-modules-extract-imports: 3.0.0(postcss@8.4.32)
postcss-modules-local-by-default: 4.0.3(postcss@8.4.31) postcss-modules-local-by-default: 4.0.3(postcss@8.4.32)
postcss-modules-scope: 3.0.0(postcss@8.4.31) postcss-modules-scope: 3.0.0(postcss@8.4.32)
postcss-modules-values: 4.0.0(postcss@8.4.31) postcss-modules-values: 4.0.0(postcss@8.4.32)
string-hash: 1.1.3 string-hash: 1.1.3
dev: true dev: true
@ -4735,11 +4752,11 @@ packages:
resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==}
dev: true dev: true
/postcss@8.4.31: /postcss@8.4.32:
resolution: {integrity: sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==} resolution: {integrity: sha512-D/kj5JNu6oo2EIy+XL/26JEDTlIbB8hw85G8StOE6L74RQAVVP5rej6wxCNqyMbR4RkPfqvezVbPw81Ngd6Kcw==}
engines: {node: ^10 || ^12 || >=14} engines: {node: ^10 || ^12 || >=14}
dependencies: dependencies:
nanoid: 3.3.6 nanoid: 3.3.7
picocolors: 1.0.0 picocolors: 1.0.0
source-map-js: 1.0.2 source-map-js: 1.0.2
@ -4914,9 +4931,9 @@ packages:
engines: {node: '>=6'} engines: {node: '>=6'}
dev: true dev: true
/puppeteer-core@21.5.1: /puppeteer-core@21.5.2:
resolution: {integrity: sha512-u6c3SZKAOaOQogaTkQvllxT/o2PP16wkbrUWINtMhfvrB4ko+xwqC1pb+vyCPMmNUh3N/CX5YGqb3DWx2fUPSQ==} resolution: {integrity: sha512-v4T0cWnujSKs+iEfmb8ccd7u4/x8oblEyKqplqKnJ582Kw8PewYAWvkH4qUWhitN3O2q9RF7dzkvjyK5HbzjLA==}
engines: {node: '>=16.3.0'} engines: {node: '>=16.13.2'}
dependencies: dependencies:
'@puppeteer/browsers': 1.8.0 '@puppeteer/browsers': 1.8.0
chromium-bidi: 0.4.33(devtools-protocol@0.0.1203626) chromium-bidi: 0.4.33(devtools-protocol@0.0.1203626)
@ -4931,14 +4948,14 @@ packages:
- utf-8-validate - utf-8-validate
dev: true dev: true
/puppeteer@21.5.1(typescript@5.2.2): /puppeteer@21.5.2(typescript@5.2.2):
resolution: {integrity: sha512-NkI06BXckVZeZUkODK+BbgGelQSu7uYEp9PaJDozxpwNRFDYoVfHQvd2G4dERoLdP6+qx4EBPwEhk4dEkQc2Kg==} resolution: {integrity: sha512-BaAGJOq8Fl6/cck6obmwaNLksuY0Bg/lIahCLhJPGXBFUD2mCffypa4A592MaWnDcye7eaHmSK9yot0pxctY8A==}
engines: {node: '>=16.3.0'} engines: {node: '>=16.13.2'}
requiresBuild: true requiresBuild: true
dependencies: dependencies:
'@puppeteer/browsers': 1.8.0 '@puppeteer/browsers': 1.8.0
cosmiconfig: 8.3.6(typescript@5.2.2) cosmiconfig: 8.3.6(typescript@5.2.2)
puppeteer-core: 21.5.1 puppeteer-core: 21.5.2
transitivePeerDependencies: transitivePeerDependencies:
- bufferutil - bufferutil
- encoding - encoding
@ -5129,7 +5146,7 @@ packages:
rollup: 4.1.4 rollup: 4.1.4
typescript: 5.2.2 typescript: 5.2.2
optionalDependencies: optionalDependencies:
'@babel/code-frame': 7.22.13 '@babel/code-frame': 7.23.5
dev: true dev: true
/rollup-plugin-esbuild@6.1.0(esbuild@0.19.5)(rollup@4.1.4): /rollup-plugin-esbuild@6.1.0(esbuild@0.19.5)(rollup@4.1.4):
@ -5771,8 +5788,8 @@ packages:
typescript: 5.2.2 typescript: 5.2.2
dev: true dev: true
/tsx@4.5.0: /tsx@4.6.2:
resolution: {integrity: sha512-hgxdziy9KLaHh9KE+a6tIZFP6kb0MLq/1D0sJVifbGP4QVEYhy6+2FNn7MyCm1pMc63p9CW/L1OzdqTNPxs6rg==} resolution: {integrity: sha512-QPpBdJo+ZDtqZgAnq86iY/PD2KYCUPSUGIunHdGwyII99GKH+f3z3FZ8XNFLSGQIA4I365ui8wnQpl8OKLqcsg==}
engines: {node: '>=18.0.0'} engines: {node: '>=18.0.0'}
hasBin: true hasBin: true
dependencies: dependencies:
@ -5954,7 +5971,7 @@ packages:
engines: {node: '>= 0.8'} engines: {node: '>= 0.8'}
dev: true dev: true
/vite-node@0.34.6(@types/node@20.10.0)(terser@5.22.0): /vite-node@0.34.6(@types/node@20.10.3)(terser@5.22.0):
resolution: {integrity: sha512-nlBMJ9x6n7/Amaz6F3zJ97EBwR2FkzhBRxF5e+jE6LA3yi6Wtc2lyTij1OnDMIr34v5g/tVQtsVAzhT0jc5ygA==} resolution: {integrity: sha512-nlBMJ9x6n7/Amaz6F3zJ97EBwR2FkzhBRxF5e+jE6LA3yi6Wtc2lyTij1OnDMIr34v5g/tVQtsVAzhT0jc5ygA==}
engines: {node: '>=v14.18.0'} engines: {node: '>=v14.18.0'}
hasBin: true hasBin: true
@ -5964,7 +5981,7 @@ packages:
mlly: 1.4.2 mlly: 1.4.2
pathe: 1.1.1 pathe: 1.1.1
picocolors: 1.0.0 picocolors: 1.0.0
vite: 5.0.0(@types/node@20.10.0)(terser@5.22.0) vite: 5.0.0(@types/node@20.10.3)(terser@5.22.0)
transitivePeerDependencies: transitivePeerDependencies:
- '@types/node' - '@types/node'
- less - less
@ -5976,7 +5993,7 @@ packages:
- terser - terser
dev: true dev: true
/vite@5.0.0(@types/node@20.10.0)(terser@5.22.0): /vite@5.0.0(@types/node@20.10.3)(terser@5.22.0):
resolution: {integrity: sha512-ESJVM59mdyGpsiNAeHQOR/0fqNoOyWPYesFto8FFZugfmhdHx8Fzd8sF3Q/xkVhZsyOxHfdM7ieiVAorI9RjFw==} resolution: {integrity: sha512-ESJVM59mdyGpsiNAeHQOR/0fqNoOyWPYesFto8FFZugfmhdHx8Fzd8sF3Q/xkVhZsyOxHfdM7ieiVAorI9RjFw==}
engines: {node: ^18.0.0 || >=20.0.0} engines: {node: ^18.0.0 || >=20.0.0}
hasBin: true hasBin: true
@ -6004,9 +6021,9 @@ packages:
terser: terser:
optional: true optional: true
dependencies: dependencies:
'@types/node': 20.10.0 '@types/node': 20.10.3
esbuild: 0.19.5 esbuild: 0.19.5
postcss: 8.4.31 postcss: 8.4.32
rollup: 4.4.1 rollup: 4.4.1
terser: 5.22.0 terser: 5.22.0
optionalDependencies: optionalDependencies:
@ -6046,7 +6063,7 @@ packages:
dependencies: dependencies:
'@types/chai': 4.3.9 '@types/chai': 4.3.9
'@types/chai-subset': 1.3.4 '@types/chai-subset': 1.3.4
'@types/node': 20.10.0 '@types/node': 20.10.3
'@vitest/expect': 0.34.6 '@vitest/expect': 0.34.6
'@vitest/runner': 0.34.6 '@vitest/runner': 0.34.6
'@vitest/snapshot': 0.34.6 '@vitest/snapshot': 0.34.6
@ -6066,8 +6083,8 @@ packages:
strip-literal: 1.3.0 strip-literal: 1.3.0
tinybench: 2.5.1 tinybench: 2.5.1
tinypool: 0.7.0 tinypool: 0.7.0
vite: 5.0.0(@types/node@20.10.0)(terser@5.22.0) vite: 5.0.0(@types/node@20.10.3)(terser@5.22.0)
vite-node: 0.34.6(@types/node@20.10.0)(terser@5.22.0) vite-node: 0.34.6(@types/node@20.10.3)(terser@5.22.0)
why-is-node-running: 2.2.2 why-is-node-running: 2.2.2
transitivePeerDependencies: transitivePeerDependencies:
- less - less
@ -6183,8 +6200,8 @@ packages:
resolution: {integrity: sha512-RNGKj82nUPg3g5ygxkQl0R937xLyho1J24ItRCBTr/m1YnZkzJy1hUiHUJrc/VlsDQzsCnInEGSg3bci0Lmd4w==} resolution: {integrity: sha512-RNGKj82nUPg3g5ygxkQl0R937xLyho1J24ItRCBTr/m1YnZkzJy1hUiHUJrc/VlsDQzsCnInEGSg3bci0Lmd4w==}
engines: {node: '>= 10.0.0'} engines: {node: '>= 10.0.0'}
dependencies: dependencies:
'@babel/parser': 7.23.4 '@babel/parser': 7.23.5
'@babel/types': 7.23.4 '@babel/types': 7.23.5
assert-never: 1.2.1 assert-never: 1.2.1
babel-walk: 3.0.0-canary-5 babel-walk: 3.0.0-canary-5
dev: true dev: true

View File

@ -2,7 +2,7 @@
// Using esbuild for faster dev builds. // Using esbuild for faster dev builds.
// We are still using Rollup for production builds because it generates // We are still using Rollup for production builds because it generates
// smaller files w/ better tree-shaking. // smaller files and provides better tree-shaking.
import esbuild from 'esbuild' import esbuild from 'esbuild'
import { resolve, relative, dirname } from 'node:path' import { resolve, relative, dirname } from 'node:path'