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

View File

@ -152,6 +152,28 @@ describe('compiler: element transform', () => {
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', () => {
const bindingMetadata = {
Example: BindingTypes.SETUP_MAYBE_REF
@ -1138,6 +1160,20 @@ describe('compiler: element transform', () => {
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', () => {

View File

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

View File

@ -19,7 +19,8 @@ import {
TemplateTextChildNode,
DirectiveArguments,
createVNodeCall,
ConstantTypes
ConstantTypes,
JSChildNode
} from '../ast'
import {
PatchFlags,
@ -370,6 +371,13 @@ function resolveSetupReference(name: string, context: TransformContext) {
`${context.helperString(UNREF)}(${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
@ -437,6 +445,12 @@ export function buildProps(
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 (
value.type === NodeTypes.JS_CACHE_EXPRESSION ||
((value.type === NodeTypes.SIMPLE_EXPRESSION ||

View File

@ -7,7 +7,8 @@ import {
NodeTypes,
ObjectExpression,
transform,
VNodeCall
VNodeCall,
BindingTypes
} from '@vue/compiler-core'
import { transformOn } from '../../src/transforms/vOn'
import { V_ON_WITH_KEYS, V_ON_WITH_MODIFIERS } from '../../src/runtimeHelpers'
@ -25,12 +26,11 @@ function parseWithVOn(template: string, options: CompilerOptions = {}) {
},
...options
})
const node = (ast.children[0] as ElementNode).codegenNode as VNodeCall
return {
root: ast,
props: (
((ast.children[0] as ElementNode).codegenNode as VNodeCall)
.props as ObjectExpression
).properties
node,
props: (node.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) {
const count = ref(0)
const style = { color: 'red' }
return (_ctx, _push, _parent, _attrs) => {
const _cssVars = { style: {
\\"--xxxxxxxx-count\\": (count.value)
\\"--xxxxxxxx-count\\": (count.value),
\\"--xxxxxxxx-style\\\\\\\\.color\\": (style.color)
}}
_push(\`<!--[--><div\${
_ssrRenderAttrs(_cssVars)

View File

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

View File

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

View File

@ -332,7 +332,7 @@ exports[`defineProps > withDefaults (dynamic) 1`] = `
import { defaults } from './foo'
export default /*#__PURE__*/_defineComponent({
props: _mergeDefaults({
props: /*#__PURE__*/_mergeDefaults({
foo: { type: String, required: false },
bar: { type: Number, required: false },
baz: { type: Boolean, required: true }
@ -353,7 +353,7 @@ exports[`defineProps > withDefaults (dynamic) w/ production mode 1`] = `
import { defaults } from './foo'
export default /*#__PURE__*/_defineComponent({
props: _mergeDefaults({
props: /*#__PURE__*/_mergeDefaults({
foo: { type: Function },
bar: { type: Boolean },
baz: { type: [Boolean, Function] },
@ -375,7 +375,7 @@ exports[`defineProps > withDefaults (reference) 1`] = `
import { defaults } from './foo'
export default /*#__PURE__*/_defineComponent({
props: _mergeDefaults({
props: /*#__PURE__*/_mergeDefaults({
foo: { type: String, required: false },
bar: { type: Number, required: false },
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'
export default /*#__PURE__*/_defineComponent({
props: _mergeDefaults({
props: /*#__PURE__*/_mergeDefaults({
foo: { type: Function, required: false }
}, {
['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'
export default {
props: _mergeDefaults(['foo', 'bar', 'baz'], {
props: /*#__PURE__*/_mergeDefaults(['foo', 'bar', 'baz'], {
foo: 1,
bar: () => ({}),
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'
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,
bar: () => ({}),
func: () => {}, __skip_func: true,
@ -101,7 +101,7 @@ exports[`sfc reactive props destructure > default values w/ runtime declaration
"import { mergeDefaults as _mergeDefaults } from 'vue'
export default {
props: _mergeDefaults(['foo', 'foo:bar'], {
props: /*#__PURE__*/_mergeDefaults(['foo', 'foo:bar'], {
foo: 1,
\\"foo:bar\\": 'foo-bar'
}),

View File

@ -48,7 +48,7 @@ describe('defineModel()', () => {
{ defineModel: true }
)
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(`const count = _useModel(__props, "modelValue")`)
expect(content).not.toMatch('defineModel')
@ -70,7 +70,7 @@ describe('defineModel()', () => {
{ defineModel: true }
)
assertCode(content)
expect(content).toMatch(`props: _mergeModels(['foo', 'bar'], {
expect(content).toMatch(`props: /*#__PURE__*/_mergeModels(['foo', 'bar'], {
"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
// function
// 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,
bar: () => ({}),
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
// is imported, or spreads another object)
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,
bar: () => ({}),
func: () => {}, __skip_func: true,
@ -122,7 +123,7 @@ describe('sfc reactive props destructure', () => {
})
expect(content).toMatch(`
props: _mergeDefaults(['foo', 'foo:bar'], {
props: /*#__PURE__*/_mergeDefaults(['foo', 'foo:bar'], {
foo: 1,
"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', () => {
test('relative ts', () => {
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', () => {
expect(compileScoped(`@media print { .foo { color: red }}`))
.toMatchInlineSnapshot(`

View File

@ -61,6 +61,33 @@ body
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', () => {
const template = parse(`<template lang="unknownLang">hi</template>\n`, {
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', () => {
const padding = Math.round(Math.random() * 10)
const custom = parse(

View File

@ -32,18 +32,18 @@
},
"homepage": "https://github.com/vuejs/core/tree/main/packages/compiler-sfc#readme",
"dependencies": {
"@babel/parser": "^7.23.4",
"@babel/parser": "^7.23.5",
"@vue/compiler-core": "workspace:*",
"@vue/compiler-dom": "workspace:*",
"@vue/compiler-ssr": "workspace:*",
"@vue/shared": "workspace:*",
"estree-walker": "^2.0.2",
"magic-string": "^0.30.5",
"postcss": "^8.4.31",
"postcss": "^8.4.32",
"source-map-js": "^1.0.2"
},
"devDependencies": {
"@babel/types": "^7.23.4",
"@babel/types": "^7.23.5",
"@vue/consolidate": "^0.17.3",
"hash-sum": "^2.0.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) {
const genMap = (block: SFCBlock | null) => {
const genMap = (block: SFCBlock | null, columnOffset = 0) => {
if (block && !block.src) {
block.map = generateSourceMap(
filename,
source,
block.content,
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)
descriptor.styles.forEach(genMap)
descriptor.customBlocks.forEach(genMap)
descriptor.styles.forEach(s => genMap(s))
descriptor.customBlocks.forEach(s => genMap(s))
}
// parse CSS vars
@ -335,7 +347,8 @@ function generateSourceMap(
source: string,
generated: string,
sourceRoot: string,
lineOffset: number
lineOffset: number,
columnOffset: number
): RawSourceMap {
const map = new SourceMapGenerator({
file: filename.replace(/\\/g, '/'),
@ -351,7 +364,7 @@ function generateSourceMap(
if (!/\s/.test(line[i])) {
map._mappings.add({
originalLine,
originalColumn: i,
originalColumn: i + columnOffset,
generatedLine,
generatedColumn: i,
source: filename,
@ -431,3 +444,31 @@ export function hmrShouldReload(
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}`))
.join(', ')}]`
emitsDecl = emitsDecl
? `${ctx.helper('mergeModels')}(${emitsDecl}, ${modelEmitsDecl})`
? `/*#__PURE__*/${ctx.helper(
'mergeModels'
)}(${emitsDecl}, ${modelEmitsDecl})`
: modelEmitsDecl
}
return emitsDecl

View File

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

View File

@ -140,7 +140,8 @@ interface ResolvedElements {
export function resolveTypeElements(
ctx: TypeResolveContext,
node: Node & MaybeWithScope & { _resolvedElements?: ResolvedElements },
scope?: TypeScope
scope?: TypeScope,
typeParameters?: Record<string, Node>
): ResolvedElements {
if (node._resolvedElements) {
return node._resolvedElements
@ -148,30 +149,37 @@ export function resolveTypeElements(
return (node._resolvedElements = innerResolveTypeElements(
ctx,
node,
node._ownerScope || scope || ctxToScope(ctx)
node._ownerScope || scope || ctxToScope(ctx),
typeParameters
))
}
function innerResolveTypeElements(
ctx: TypeResolveContext,
node: Node,
scope: TypeScope
scope: TypeScope,
typeParameters?: Record<string, Node>
): ResolvedElements {
switch (node.type) {
case 'TSTypeLiteral':
return typeElementsToMap(ctx, node.members, scope)
return typeElementsToMap(ctx, node.members, scope, typeParameters)
case 'TSInterfaceDeclaration':
return resolveInterfaceMembers(ctx, node, scope)
return resolveInterfaceMembers(ctx, node, scope, typeParameters)
case 'TSTypeAliasDeclaration':
case 'TSParenthesizedType':
return resolveTypeElements(ctx, node.typeAnnotation, scope)
return resolveTypeElements(
ctx,
node.typeAnnotation,
scope,
typeParameters
)
case 'TSFunctionType': {
return { props: {}, calls: [node] }
}
case 'TSUnionType':
case 'TSIntersectionType':
return mergeElements(
node.types.map(t => resolveTypeElements(ctx, t, scope)),
node.types.map(t => resolveTypeElements(ctx, t, scope, typeParameters)),
node.type
)
case 'TSMappedType':
@ -193,20 +201,57 @@ function innerResolveTypeElements(
scope.imports[typeName]?.source === 'vue'
) {
return resolveExtractPropTypes(
resolveTypeElements(ctx, node.typeParameters.params[0], scope),
resolveTypeElements(
ctx,
node.typeParameters.params[0],
scope,
typeParameters
),
scope
)
}
const resolved = resolveTypeReference(ctx, node, scope)
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 {
if (typeof typeName === 'string') {
if (typeParameters && typeParameters[typeName]) {
return resolveTypeElements(
ctx,
typeParameters[typeName],
scope,
typeParameters
)
}
if (
// @ts-ignore
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) {
// limited support, only reference types
const ret = resolveReturnType(
@ -265,11 +310,17 @@ function innerResolveTypeElements(
function typeElementsToMap(
ctx: TypeResolveContext,
elements: TSTypeElement[],
scope = ctxToScope(ctx)
scope = ctxToScope(ctx),
typeParameters?: Record<string, Node>
): ResolvedElements {
const res: ResolvedElements = { props: {} }
for (const e of elements) {
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
const name = getId(e.key)
if (name && !e.computed) {
@ -345,9 +396,15 @@ function createProperty(
function resolveInterfaceMembers(
ctx: TypeResolveContext,
node: TSInterfaceDeclaration & MaybeWithScope,
scope: TypeScope
scope: TypeScope,
typeParameters?: Record<string, Node>
): ResolvedElements {
const base = typeElementsToMap(ctx, node.body.body, node._ownerScope)
const base = typeElementsToMap(
ctx,
node.body.body,
node._ownerScope,
typeParameters
)
if (node.extends) {
for (const ext of node.extends) {
if (
@ -565,9 +622,15 @@ function resolveBuiltin(
ctx: TypeResolveContext,
node: TSTypeReference | TSExpressionWithTypeArguments,
name: GetSetType<typeof SupportedBuiltinsSet>,
scope: TypeScope
scope: TypeScope,
typeParameters?: Record<string, Node>
): ResolvedElements {
const t = resolveTypeElements(ctx, node.typeParameters!.params[0], scope)
const t = resolveTypeElements(
ctx,
node.typeParameters!.params[0],
scope,
typeParameters
)
switch (name) {
case 'Partial': {
const res: ResolvedElements = { props: {}, calls: t.calls }
@ -741,7 +804,25 @@ let loadTS: (() => typeof TS) | undefined
* @private
*/
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']>
@ -790,7 +871,12 @@ function importSourceToScope(
scope: TypeScope,
source: string
): 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) {
return ctx.error(
`No fs option provided to \`compileScript\` in non-Node environment. ` +
@ -1102,14 +1188,7 @@ function moduleDeclToScope(
return node._resolvedChildScope
}
const scope = new TypeScope(
parentScope.filename,
parentScope.source,
parentScope.offset,
Object.create(parentScope.imports),
Object.create(parentScope.types),
Object.create(parentScope.declares)
)
const scope = createChildScope(parentScope)
if (node.body.type === 'TSModuleDeclaration') {
const decl = node.body as TSModuleDeclaration & WithScope
@ -1123,6 +1202,17 @@ function moduleDeclToScope(
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/
function recordTypes(
@ -1261,7 +1351,7 @@ function recordType(
if (overwriteId || node.id) types[overwriteId || getId(node.id!)] = node
break
case 'TSTypeAliasDeclaration':
types[node.id.name] = node.typeAnnotation
types[node.id.name] = node.typeParameters ? node : node.typeAnnotation
break
case 'TSDeclareFunction':
if (node.id) declares[node.id.name] = node

View File

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

View File

@ -22,17 +22,25 @@ export function genCssVarsFromList(
): string {
return `{\n ${vars
.map(
key => `"${isSSR ? `--` : ``}${genVarName(id, key, isProd)}": (${key})`
key =>
`"${isSSR ? `--` : ``}${genVarName(id, key, isProd, isSSR)}": (${key})`
)
.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) {
return hash(id + raw)
} else {
// 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
}
})
if (n.type === 'pseudo' && (n.value === ':is' || n.value === ':where')) {
rewriteSelector(id, n.nodes[0], selectorRoot, slotted)
if (node) {
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
}
})
}
if (node) {
;(node as selectorParser.Node).spaces.after = ''

View File

@ -11,7 +11,9 @@ import {
h,
SlotsType,
Slots,
VNode
VNode,
withKeys,
withModifiers
} from 'vue'
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)
})
describe('withKeys and withModifiers as pro', () => {
const onKeydown = withKeys(e => {}, [''])
const onClick = withModifiers(e => {}, [''])
;<input onKeydown={onKeydown} onClick={onClick} />
})
import {
DefineComponent,
ComponentOptionsMixin,

View File

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

View File

@ -62,3 +62,31 @@ describe('should unwrap tuple correctly', () => {
const reactiveTuple = reactive(tuple)
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 { 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 WeakCollections = WeakMap<any, any> | WeakSet<any>

View File

@ -16,7 +16,6 @@ import {
isShallow
} from './reactive'
import type { ShallowReactiveMarker } from './reactive'
import { CollectionTypes } from './collectionHandlers'
import { createDep, Dep } from './dep'
import { ComputedRefImpl } from './computed'
import { getDepFromReactive } from './reactiveEffect'
@ -505,16 +504,23 @@ export type UnwrapRef<T> = T extends ShallowRef<infer V>
export type UnwrapRefSimple<T> = T extends
| Function
| CollectionTypes
| BaseTypes
| Ref
| RefUnwrapBailTypes[keyof RefUnwrapBailTypes]
| { [RawSymbol]?: true }
? T
: 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
: T extends Map<infer K, infer V>
? Map<K, UnwrapRefSimple<V>>
: T extends WeakMap<infer K, infer V>
? WeakMap<K, UnwrapRefSimple<V>>
: T extends Set<infer V>
? Set<UnwrapRefSimple<V>>
: 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,
EmitFn,
emit,
normalizeEmitsOptions
normalizeEmitsOptions,
EmitsToProps
} from './componentEmits'
import {
EMPTY_OBJ,
@ -131,7 +132,7 @@ export interface FunctionalComponent<
> extends ComponentInternalOptions {
// 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'>
): any
props?: ComponentPropsOptions<P>

View File

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

View File

@ -11,9 +11,9 @@ export function initCustomFormatter() {
}
const vueStyle = { style: 'color:#3ba776' }
const numberStyle = { style: 'color:#0b1bc9' }
const stringStyle = { style: 'color:#b62e24' }
const keywordStyle = { style: 'color:#9d288c' }
const numberStyle = { style: 'color:#1677ff' }
const stringStyle = { style: 'color:#f5222d' }
const keywordStyle = { style: 'color:#eb2f96' }
// custom formatter for Chrome
// 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')
})
// #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', () => {
const el = document.createElement('div')
patchProp(el, 'translate', null, 'no')

View File

@ -32,14 +32,22 @@ const modifierGuards: Record<
/**
* @private
*/
export const withModifiers = (fn: Function, modifiers: string[]) => {
return (event: Event, ...args: unknown[]) => {
for (let i = 0; i < modifiers.length; i++) {
const guard = modifierGuards[modifiers[i]]
if (guard && guard(event, modifiers)) return
}
return fn(event, ...args)
}
export const withModifiers = <
T extends (event: Event, ...args: unknown[]) => any
>(
fn: T & { _withMods?: T },
modifiers: string[]
) => {
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.
@ -57,7 +65,10 @@ const keyNames: Record<string, string | string[]> = {
/**
* @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 instance: ComponentInternalInstance | null = null
if (__COMPAT__) {
@ -77,40 +88,43 @@ export const withKeys = (fn: Function, modifiers: string[]) => {
}
}
return (event: KeyboardEvent) => {
if (!('key' in event)) {
return
}
return (
fn._withKeys ||
(fn._withKeys = (event => {
if (!('key' in event)) {
return
}
const eventKey = hyphenate(event.key)
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)
) {
const eventKey = hyphenate(event.key)
if (modifiers.some(k => k === eventKey || keyNames[k] === eventKey)) {
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)
if (__COMPAT__) {
const keyCode = String(event.keyCode)
if (
compatUtils.isCompatEnabled(
DeprecationTypes.V_ON_KEYCODE_MODIFIER,
instance
) &&
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 { 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>
@ -73,7 +78,7 @@ function shouldSetAsProp(
return true
}
// 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 false
@ -105,8 +110,19 @@ function shouldSetAsProp(
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
if (nativeOnRE.test(key) && isString(value)) {
if (isNativeOn(key) && isString(value)) {
return false
}

View File

@ -17,7 +17,10 @@ import {
renderSlot,
onErrorCaptured,
onServerPrefetch,
getCurrentInstance
getCurrentInstance,
reactive,
computed,
createSSRApp
} from 'vue'
import { escapeHtml } from '@vue/shared'
import { renderToString } from '../src/renderToString'
@ -1140,5 +1143,47 @@ function testRender(type: string, render: typeof renderToString) {
expect(renderError).toBe(null)
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.
if (!msg.value) await store.fetchData()
expect(msg.value).toBe('hello world')
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
// since there cannot be state mutations during render.
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

View File

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

View File

@ -1,8 +1,22 @@
<script setup lang="ts">
import Header from './Header.vue'
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 { 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 = () => {
document.documentElement.style.setProperty('--vh', window.innerHeight + `px`)
@ -97,8 +111,9 @@ onMounted(() => {
@toggle-ssr="toggleSSR"
/>
<Repl
v-if="EditorComponent"
:theme="theme"
:editor="Monaco"
:editor="EditorComponent"
@keydown.ctrl.s.prevent
@keydown.meta.s.prevent
:ssr="useSSRMode"

View File

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

View File

@ -12,8 +12,11 @@ export const NOOP = () => {}
*/
export const NO = () => false
const onRE = /^on[^a-z]/
export const isOn = (key: string) => onRE.test(key)
export const isOn = (key: string) =>
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:')

View File

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

View File

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

View File

@ -1586,6 +1586,72 @@ describe('e2e: Transition', () => {
expect(barMountSpy).toBeCalledTimes(1)
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', () => {

View File

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

View File

@ -4,14 +4,32 @@ import { initDev } from './dev'
import { compile, CompilerOptions, CompilerError } from '@vue/compiler-dom'
import { registerRuntimeCompiler, RenderFunction, warn } 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'
if (__DEV__) {
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(
template: string | HTMLElement,
@ -27,7 +45,8 @@ function compileToFunction(
}
const key = template
const cached = compileCache[key]
const cache = getCache(options)
const cached = cache[key]
if (cached) {
return cached
}
@ -84,7 +103,7 @@ function compileToFunction(
// mark the function as runtime compiled
;(render as InternalRenderFunction)._rc = true
return (compileCache[key] = render)
return (cache[key] = render)
}
registerRuntimeCompiler(compileToFunction)

View File

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

View File

@ -2,7 +2,7 @@
// Using esbuild for faster dev builds.
// 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 { resolve, relative, dirname } from 'node:path'