Skip to content

Commit 9320020

Browse files
Merge branch 'vuejs:main' into feat/apiWatch/depth
2 parents 87348ec + 4d94ebf commit 9320020

File tree

17 files changed

+215
-76
lines changed

17 files changed

+215
-76
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@
9393
"prettier": "^3.1.0",
9494
"pretty-bytes": "^6.1.1",
9595
"pug": "^3.0.2",
96-
"puppeteer": "~21.5.1",
96+
"puppeteer": "~21.5.2",
9797
"rimraf": "^5.0.5",
9898
"rollup": "^4.1.4",
9999
"rollup-plugin-dts": "^6.1.0",

packages/compiler-core/__tests__/transforms/transformElement.spec.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,28 @@ describe('compiler: element transform', () => {
152152
expect(node.tag).toBe(`Foo.Example`)
153153
})
154154

155+
test('resolve namespaced component from props bindings (inline)', () => {
156+
const { root, node } = parseWithElementTransform(`<Foo.Example/>`, {
157+
inline: true,
158+
bindingMetadata: {
159+
Foo: BindingTypes.PROPS
160+
}
161+
})
162+
expect(root.helpers).not.toContain(RESOLVE_COMPONENT)
163+
expect(node.tag).toBe(`_unref(__props["Foo"]).Example`)
164+
})
165+
166+
test('resolve namespaced component from props bindings (non-inline)', () => {
167+
const { root, node } = parseWithElementTransform(`<Foo.Example/>`, {
168+
inline: false,
169+
bindingMetadata: {
170+
Foo: BindingTypes.PROPS
171+
}
172+
})
173+
expect(root.helpers).not.toContain(RESOLVE_COMPONENT)
174+
expect(node.tag).toBe('_unref($props["Foo"]).Example')
175+
})
176+
155177
test('do not resolve component from non-script-setup bindings', () => {
156178
const bindingMetadata = {
157179
Example: BindingTypes.SETUP_MAYBE_REF
@@ -1138,6 +1160,20 @@ describe('compiler: element transform', () => {
11381160
genFlagText([PatchFlags.PROPS, PatchFlags.NEED_HYDRATION])
11391161
)
11401162
})
1163+
1164+
test('should not have PROPS patchflag for constant v-on handlers', () => {
1165+
const { node } = parseWithElementTransform(`<div @keydown="foo" />`, {
1166+
prefixIdentifiers: true,
1167+
bindingMetadata: {
1168+
foo: BindingTypes.SETUP_CONST
1169+
},
1170+
directiveTransforms: {
1171+
on: transformOn
1172+
}
1173+
})
1174+
// should only have hydration flag
1175+
expect(node.patchFlag).toBe(genFlagText(PatchFlags.NEED_HYDRATION))
1176+
})
11411177
})
11421178

11431179
describe('dynamic component', () => {

packages/compiler-core/src/transforms/transformElement.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@ import {
1919
TemplateTextChildNode,
2020
DirectiveArguments,
2121
createVNodeCall,
22-
ConstantTypes
22+
ConstantTypes,
23+
JSChildNode
2324
} from '../ast'
2425
import {
2526
PatchFlags,
@@ -370,6 +371,13 @@ function resolveSetupReference(name: string, context: TransformContext) {
370371
`${context.helperString(UNREF)}(${fromMaybeRef})`
371372
: `$setup[${JSON.stringify(fromMaybeRef)}]`
372373
}
374+
375+
const fromProps = checkType(BindingTypes.PROPS)
376+
if (fromProps) {
377+
return `${context.helperString(UNREF)}(${
378+
context.inline ? '__props' : '$props'
379+
}[${JSON.stringify(fromProps)}])`
380+
}
373381
}
374382

375383
export type PropsExpression = ObjectExpression | CallExpression | ExpressionNode
@@ -437,6 +445,12 @@ export function buildProps(
437445
hasVnodeHook = true
438446
}
439447

448+
if (isEventHandler && value.type === NodeTypes.JS_CALL_EXPRESSION) {
449+
// handler wrapped with internal helper e.g. withModifiers(fn)
450+
// extract the actual expression
451+
value = value.arguments[0] as JSChildNode
452+
}
453+
440454
if (
441455
value.type === NodeTypes.JS_CACHE_EXPRESSION ||
442456
((value.type === NodeTypes.SIMPLE_EXPRESSION ||

packages/compiler-dom/__tests__/transforms/vOn.spec.ts

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ import {
77
NodeTypes,
88
ObjectExpression,
99
transform,
10-
VNodeCall
10+
VNodeCall,
11+
BindingTypes
1112
} from '@vue/compiler-core'
1213
import { transformOn } from '../../src/transforms/vOn'
1314
import { V_ON_WITH_KEYS, V_ON_WITH_MODIFIERS } from '../../src/runtimeHelpers'
@@ -25,12 +26,11 @@ function parseWithVOn(template: string, options: CompilerOptions = {}) {
2526
},
2627
...options
2728
})
29+
const node = (ast.children[0] as ElementNode).codegenNode as VNodeCall
2830
return {
2931
root: ast,
30-
props: (
31-
((ast.children[0] as ElementNode).codegenNode as VNodeCall)
32-
.props as ObjectExpression
33-
).properties
32+
node,
33+
props: (node.props as ObjectExpression).properties
3434
}
3535
}
3636

@@ -288,4 +288,18 @@ describe('compiler-dom: transform v-on', () => {
288288
}
289289
})
290290
})
291+
292+
test('should not have PROPS patchFlag for constant v-on handlers with modifiers', () => {
293+
const { node } = parseWithVOn(`<div @keydown.up="foo" />`, {
294+
prefixIdentifiers: true,
295+
bindingMetadata: {
296+
foo: BindingTypes.SETUP_CONST
297+
},
298+
directiveTransforms: {
299+
on: transformOn
300+
}
301+
})
302+
// should only have hydration flag
303+
expect(node.patchFlag).toBe(genFlagText(PatchFlags.NEED_HYDRATION))
304+
})
291305
})

packages/compiler-sfc/__tests__/__snapshots__/compileScript.spec.ts.snap

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1020,10 +1020,12 @@ export default {
10201020
setup(__props) {
10211021
10221022
const count = ref(0)
1023+
const style = { color: 'red' }
10231024
10241025
return (_ctx, _push, _parent, _attrs) => {
10251026
const _cssVars = { style: {
1026-
\\"--xxxxxxxx-count\\": (count.value)
1027+
\\"--xxxxxxxx-count\\": (count.value),
1028+
\\"--xxxxxxxx-style\\\\\\\\.color\\": (style.color)
10271029
}}
10281030
_push(\`<!--[--><div\${
10291031
_ssrRenderAttrs(_cssVars)

packages/compiler-sfc/__tests__/compileScript.spec.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -786,13 +786,15 @@ describe('SFC compile <script setup>', () => {
786786
<script setup>
787787
import { ref } from 'vue'
788788
const count = ref(0)
789+
const style = { color: 'red' }
789790
</script>
790791
<template>
791792
<div>{{ count }}</div>
792793
<div>static</div>
793794
</template>
794795
<style>
795796
div { color: v-bind(count) }
797+
span { color: v-bind(style.color) }
796798
</style>
797799
`,
798800
{
@@ -807,6 +809,7 @@ describe('SFC compile <script setup>', () => {
807809
expect(content).toMatch(`ssrInterpolate`)
808810
expect(content).not.toMatch(`useCssVars`)
809811
expect(content).toMatch(`"--${mockId}-count": (count.value)`)
812+
expect(content).toMatch(`"--${mockId}-style\\\\.color": (style.color)`)
810813
assertCode(content)
811814
})
812815

packages/compiler-sfc/src/script/resolveType.ts

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -741,7 +741,25 @@ let loadTS: (() => typeof TS) | undefined
741741
* @private
742742
*/
743743
export function registerTS(_loadTS: () => typeof TS) {
744-
loadTS = _loadTS
744+
loadTS = () => {
745+
try {
746+
return _loadTS()
747+
} catch (err: any) {
748+
if (
749+
typeof err.message === 'string' &&
750+
err.message.includes('Cannot find module')
751+
) {
752+
throw new Error(
753+
'Failed to load TypeScript, which is required for resolving imported types. ' +
754+
'Please make sure "typescript" is installed as a project dependency.'
755+
)
756+
} else {
757+
throw new Error(
758+
'Failed to load TypeScript for resolving imported types.'
759+
)
760+
}
761+
}
762+
}
745763
}
746764

747765
type FS = NonNullable<SFCScriptCompileOptions['fs']>
@@ -790,7 +808,12 @@ function importSourceToScope(
790808
scope: TypeScope,
791809
source: string
792810
): TypeScope {
793-
const fs = resolveFS(ctx)
811+
let fs: FS | undefined
812+
try {
813+
fs = resolveFS(ctx)
814+
} catch (err: any) {
815+
return ctx.error(err.message, node, scope)
816+
}
794817
if (!fs) {
795818
return ctx.error(
796819
`No fs option provided to \`compileScript\` in non-Node environment. ` +

packages/compiler-sfc/src/script/utils.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,8 @@ export function getEscapedPropName(key: string) {
121121

122122
export const cssVarNameEscapeSymbolsRE = /[ !"#$%&'()*+,./:;<=>?@[\\\]^`{|}~]/g
123123

124-
export function getEscapedCssVarName(key: string) {
125-
return key.replace(cssVarNameEscapeSymbolsRE, s => `\\${s}`)
124+
export function getEscapedCssVarName(key: string, doubleEscape: boolean) {
125+
return key.replace(cssVarNameEscapeSymbolsRE, s =>
126+
doubleEscape ? `\\\\${s}` : `\\${s}`
127+
)
126128
}

packages/compiler-sfc/src/style/cssVars.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,17 +22,25 @@ export function genCssVarsFromList(
2222
): string {
2323
return `{\n ${vars
2424
.map(
25-
key => `"${isSSR ? `--` : ``}${genVarName(id, key, isProd)}": (${key})`
25+
key =>
26+
`"${isSSR ? `--` : ``}${genVarName(id, key, isProd, isSSR)}": (${key})`
2627
)
2728
.join(',\n ')}\n}`
2829
}
2930

30-
function genVarName(id: string, raw: string, isProd: boolean): string {
31+
function genVarName(
32+
id: string,
33+
raw: string,
34+
isProd: boolean,
35+
isSSR = false
36+
): string {
3137
if (isProd) {
3238
return hash(id + raw)
3339
} else {
3440
// escape ASCII Punctuation & Symbols
35-
return `${id}-${getEscapedCssVarName(raw)}`
41+
// #7823 need to double-escape in SSR because the attributes are rendered
42+
// into an HTML string
43+
return `${id}-${getEscapedCssVarName(raw, isSSR)}`
3644
}
3745
}
3846

packages/runtime-dom/__tests__/patchProps.spec.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,15 @@ describe('runtime-dom: props patching', () => {
291291
expect(el.value).toBe('baz')
292292
})
293293

294+
// #8780
295+
test('embedded tag with width and height', () => {
296+
// Width and height of some embedded element such as img、video、source、canvas
297+
// must be set as attribute
298+
const el = document.createElement('img')
299+
patchProp(el, 'width', null, '24px')
300+
expect(el.getAttribute('width')).toBe('24px')
301+
})
302+
294303
test('translate attribute', () => {
295304
const el = document.createElement('div')
296305
patchProp(el, 'translate', null, 'no')

packages/runtime-dom/src/directives/vOn.ts

Lines changed: 50 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,20 @@ const modifierGuards: Record<
3232
/**
3333
* @private
3434
*/
35-
export const withModifiers = (fn: Function, modifiers: string[]) => {
36-
return (event: Event, ...args: unknown[]) => {
37-
for (let i = 0; i < modifiers.length; i++) {
38-
const guard = modifierGuards[modifiers[i]]
39-
if (guard && guard(event, modifiers)) return
40-
}
41-
return fn(event, ...args)
42-
}
35+
export const withModifiers = (
36+
fn: Function & { _withMods?: Function },
37+
modifiers: string[]
38+
) => {
39+
return (
40+
fn._withMods ||
41+
(fn._withMods = (event: Event, ...args: unknown[]) => {
42+
for (let i = 0; i < modifiers.length; i++) {
43+
const guard = modifierGuards[modifiers[i]]
44+
if (guard && guard(event, modifiers)) return
45+
}
46+
return fn(event, ...args)
47+
})
48+
)
4349
}
4450

4551
// Kept for 2.x compat.
@@ -57,7 +63,10 @@ const keyNames: Record<string, string | string[]> = {
5763
/**
5864
* @private
5965
*/
60-
export const withKeys = (fn: Function, modifiers: string[]) => {
66+
export const withKeys = (
67+
fn: Function & { _withKeys?: Function },
68+
modifiers: string[]
69+
) => {
6170
let globalKeyCodes: LegacyConfig['keyCodes']
6271
let instance: ComponentInternalInstance | null = null
6372
if (__COMPAT__) {
@@ -77,40 +86,43 @@ export const withKeys = (fn: Function, modifiers: string[]) => {
7786
}
7887
}
7988

80-
return (event: KeyboardEvent) => {
81-
if (!('key' in event)) {
82-
return
83-
}
84-
85-
const eventKey = hyphenate(event.key)
86-
if (modifiers.some(k => k === eventKey || keyNames[k] === eventKey)) {
87-
return fn(event)
88-
}
89+
return (
90+
fn._withKeys ||
91+
(fn._withKeys = (event: KeyboardEvent) => {
92+
if (!('key' in event)) {
93+
return
94+
}
8995

90-
if (__COMPAT__) {
91-
const keyCode = String(event.keyCode)
92-
if (
93-
compatUtils.isCompatEnabled(
94-
DeprecationTypes.V_ON_KEYCODE_MODIFIER,
95-
instance
96-
) &&
97-
modifiers.some(mod => mod == keyCode)
98-
) {
96+
const eventKey = hyphenate(event.key)
97+
if (modifiers.some(k => k === eventKey || keyNames[k] === eventKey)) {
9998
return fn(event)
10099
}
101-
if (globalKeyCodes) {
102-
for (const mod of modifiers) {
103-
const codes = globalKeyCodes[mod]
104-
if (codes) {
105-
const matches = isArray(codes)
106-
? codes.some(code => String(code) === keyCode)
107-
: String(codes) === keyCode
108-
if (matches) {
109-
return fn(event)
100+
101+
if (__COMPAT__) {
102+
const keyCode = String(event.keyCode)
103+
if (
104+
compatUtils.isCompatEnabled(
105+
DeprecationTypes.V_ON_KEYCODE_MODIFIER,
106+
instance
107+
) &&
108+
modifiers.some(mod => mod == keyCode)
109+
) {
110+
return fn(event)
111+
}
112+
if (globalKeyCodes) {
113+
for (const mod of modifiers) {
114+
const codes = globalKeyCodes[mod]
115+
if (codes) {
116+
const matches = isArray(codes)
117+
? codes.some(code => String(code) === keyCode)
118+
: String(codes) === keyCode
119+
if (matches) {
120+
return fn(event)
121+
}
110122
}
111123
}
112124
}
113125
}
114-
}
115-
}
126+
})
127+
)
116128
}

0 commit comments

Comments
 (0)