Skip to content

Commit 241cabf

Browse files
authored
Merge branch 'main' into GH-12964
2 parents 1f9b82f + c4a88cd commit 241cabf

File tree

26 files changed

+426
-48
lines changed

26 files changed

+426
-48
lines changed

packages-private/dts-test/directives.test-d.ts

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ type ExtractBinding<T> = T extends (
1313
declare function testDirective<
1414
Value,
1515
Modifiers extends string = string,
16-
Arg extends string = string,
16+
Arg = any,
1717
>(): ExtractBinding<Directive<any, Value, Modifiers, Arg>>
1818

1919
describe('vmodel', () => {
@@ -44,15 +44,37 @@ describe('custom', () => {
4444
value: number
4545
oldValue: number | null
4646
arg?: 'Arg'
47-
modifiers: Record<'a' | 'b', boolean>
47+
modifiers: Partial<Record<'a' | 'b', boolean>>
4848
// @ts-expect-error
4949
}>(testDirective<number, 'a' | 'b', 'Argx'>())
5050

5151
expectType<{
5252
value: number
5353
oldValue: number | null
5454
arg?: 'Arg'
55-
modifiers: Record<'a' | 'b', boolean>
55+
modifiers: Partial<Record<'a' | 'b', boolean>>
5656
// @ts-expect-error
5757
}>(testDirective<string, 'a' | 'b', 'Arg'>())
58+
59+
expectType<{
60+
value: number
61+
oldValue: number | null
62+
arg?: HTMLElement
63+
modifiers: Partial<Record<'a' | 'b', boolean>>
64+
}>(testDirective<number, 'a' | 'b', HTMLElement>())
65+
66+
expectType<{
67+
value: number
68+
oldValue: number | null
69+
arg?: HTMLElement
70+
modifiers: Partial<Record<'a' | 'b', boolean>>
71+
// @ts-expect-error
72+
}>(testDirective<number, 'a' | 'b', string>())
73+
74+
expectType<{
75+
value: number
76+
oldValue: number | null
77+
arg?: HTMLElement
78+
modifiers: Partial<Record<'a' | 'b', boolean>>
79+
}>(testDirective<number, 'a' | 'b'>())
5880
})

packages/compiler-dom/__tests__/transforms/__snapshots__/stringifyStatic.spec.ts.snap

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,24 @@ return function render(_ctx, _cache) {
1212
}"
1313
`;
1414

15+
exports[`stringify static html > eligible content + v-once node 1`] = `
16+
"const { setBlockTracking: _setBlockTracking, toDisplayString: _toDisplayString, createTextVNode: _createTextVNode, createElementVNode: _createElementVNode, createStaticVNode: _createStaticVNode, openBlock: _openBlock, createElementBlock: _createElementBlock } = Vue
17+
18+
return function render(_ctx, _cache) {
19+
return (_openBlock(), _createElementBlock("div", null, [
20+
_cache[0] || (
21+
_setBlockTracking(-1, true),
22+
(_cache[0] = _createElementVNode("div", null, [
23+
_createTextVNode(_toDisplayString(_ctx.msg), 1 /* TEXT */)
24+
])).cacheIndex = 0,
25+
_setBlockTracking(1),
26+
_cache[0]
27+
),
28+
_cache[1] || (_cache[1] = _createStaticVNode("<span class=\\"foo\\">foo</span><span class=\\"foo\\">foo</span><span class=\\"foo\\">foo</span><span class=\\"foo\\">foo</span><span class=\\"foo\\">foo</span>", 5))
29+
]))
30+
}"
31+
`;
32+
1533
exports[`stringify static html > escape 1`] = `
1634
"const { toDisplayString: _toDisplayString, normalizeClass: _normalizeClass, createElementVNode: _createElementVNode, createStaticVNode: _createStaticVNode, openBlock: _openBlock, createElementBlock: _createElementBlock } = Vue
1735

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -525,4 +525,14 @@ describe('stringify static html', () => {
525525

526526
expect(code).toMatchSnapshot()
527527
})
528+
529+
test('eligible content + v-once node', () => {
530+
const { code } = compileWithStringify(
531+
`<div>
532+
<div v-once>{{ msg }}</div>
533+
${repeat(`<span class="foo">foo</span>`, StringifyThresholds.ELEMENT_WITH_BINDING_COUNT)}
534+
</div>`,
535+
)
536+
expect(code).toMatchSnapshot()
537+
})
528538
})

packages/compiler-dom/src/transforms/stringifyStatic.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import {
1717
type TextCallNode,
1818
type TransformContext,
1919
createCallExpression,
20+
findDir,
2021
isStaticArgOf,
2122
} from '@vue/compiler-core'
2223
import {
@@ -213,6 +214,11 @@ function analyzeNode(node: StringifiableNode): [number, number] | false {
213214
return false
214215
}
215216

217+
// v-once nodes should not be stringified
218+
if (node.type === NodeTypes.ELEMENT && findDir(node, 'once', true)) {
219+
return false
220+
}
221+
216222
if (node.type === NodeTypes.TEXT_CALL) {
217223
return [1, 0]
218224
}

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

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,27 @@ export function render(_ctx, _cache) {
1212
}"
1313
`;
1414

15+
exports[`prefixing props edge case in inline mode 1`] = `
16+
"import { defineComponent as _defineComponent } from 'vue'
17+
import { unref as _unref, openBlock as _openBlock, createBlock as _createBlock } from "vue"
18+
19+
20+
export default /*@__PURE__*/_defineComponent({
21+
props: {
22+
Foo: { type: Object, required: true }
23+
},
24+
setup(__props: any) {
25+
26+
27+
28+
return (_ctx: any,_cache: any) => {
29+
return (_openBlock(), _createBlock(_unref(__props["Foo"]).Bar))
30+
}
31+
}
32+
33+
})"
34+
`;
35+
1536
exports[`should not hoist srcset URLs in SSR mode 1`] = `
1637
"import { resolveComponent as _resolveComponent, withCtx as _withCtx, createVNode as _createVNode } from "vue"
1738
import { ssrRenderAttr as _ssrRenderAttr, ssrRenderComponent as _ssrRenderComponent } from "vue/server-renderer"

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

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,17 @@ describe('resolveType', () => {
149149
})
150150
})
151151

152+
test('TSPropertySignature with ignore', () => {
153+
expect(
154+
resolve(`
155+
type Foo = string
156+
defineProps<{ foo: /* @vue-ignore */ Foo }>()
157+
`).props,
158+
).toStrictEqual({
159+
foo: ['Unknown'],
160+
})
161+
})
162+
152163
// #7553
153164
test('union type', () => {
154165
expect(
@@ -731,6 +742,22 @@ describe('resolveType', () => {
731742
})
732743
})
733744

745+
test('TSMappedType with indexed access', () => {
746+
const { props } = resolve(
747+
`
748+
type Prettify<T> = { [K in keyof T]: T[K] } & {}
749+
type Side = 'top' | 'right' | 'bottom' | 'left'
750+
type AlignedPlacement = \`\${Side}-\${Alignment}\`
751+
type Alignment = 'start' | 'end'
752+
type Placement = Prettify<Side | AlignedPlacement>
753+
defineProps<{placement?: Placement}>()
754+
`,
755+
)
756+
expect(props).toStrictEqual({
757+
placement: ['String', 'Object'],
758+
})
759+
})
760+
734761
describe('type alias declaration', () => {
735762
// #13240
736763
test('function type', () => {

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

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -512,3 +512,22 @@ test('non-identifier expression in legacy filter syntax', () => {
512512
babelParse(compilationResult.code, { sourceType: 'module' })
513513
}).not.toThrow()
514514
})
515+
516+
test('prefixing props edge case in inline mode', () => {
517+
const src = `
518+
<script setup lang="ts">
519+
defineProps<{ Foo: { Bar: unknown } }>()
520+
</script>
521+
<template>
522+
<Foo.Bar/>
523+
</template>
524+
`
525+
const { descriptor } = parse(src)
526+
const { content } = compileScript(descriptor, {
527+
id: 'xxx',
528+
inlineTemplate: true,
529+
})
530+
531+
expect(content).toMatchSnapshot()
532+
expect(content).toMatch(`__props["Foo"]).Bar`)
533+
})

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -109,8 +109,8 @@ describe('CSS vars injection', () => {
109109
{ isProd: true },
110110
)
111111
expect(content).toMatch(`_useCssVars(_ctx => ({
112-
"4003f1a6": (_ctx.color),
113-
"41b6490a": (_ctx.font.size)
112+
"v4003f1a6": (_ctx.color),
113+
"v41b6490a": (_ctx.font.size)
114114
}))}`)
115115

116116
const { code } = compileStyle({
@@ -124,8 +124,8 @@ describe('CSS vars injection', () => {
124124
})
125125
expect(code).toMatchInlineSnapshot(`
126126
".foo {
127-
color: var(--4003f1a6);
128-
font-size: var(--41b6490a);
127+
color: var(--v4003f1a6);
128+
font-size: var(--v41b6490a);
129129
}"
130130
`)
131131
})

packages/compiler-sfc/src/compileScript.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -833,6 +833,8 @@ export function compileScript(
833833
let templateMap
834834
// 9. generate return statement
835835
let returned
836+
// ensure props bindings register before compile template in inline mode
837+
const propsDecl = genRuntimeProps(ctx)
836838
if (
837839
!options.inlineTemplate ||
838840
(!sfc.template && ctx.hasDefaultExportRender)
@@ -965,7 +967,6 @@ export function compileScript(
965967
runtimeOptions += `\n __ssrInlineRender: true,`
966968
}
967969

968-
const propsDecl = genRuntimeProps(ctx)
969970
if (propsDecl) runtimeOptions += `\n props: ${propsDecl},`
970971

971972
const emitsDecl = genRuntimeEmits(ctx)

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

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1515,6 +1515,13 @@ export function inferRuntimeType(
15151515
isKeyOf = false,
15161516
typeParameters?: Record<string, Node>,
15171517
): string[] {
1518+
if (
1519+
node.leadingComments &&
1520+
node.leadingComments.some(c => c.value.includes('@vue-ignore'))
1521+
) {
1522+
return [UNKNOWN_TYPE]
1523+
}
1524+
15181525
try {
15191526
switch (node.type) {
15201527
case 'TSStringKeyword':
@@ -1781,6 +1788,47 @@ export function inferRuntimeType(
17811788
typeParameters,
17821789
).filter(t => t !== UNKNOWN_TYPE)
17831790
}
1791+
case 'TSMappedType': {
1792+
// only support { [K in keyof T]: T[K] }
1793+
const { typeAnnotation, typeParameter } = node
1794+
if (
1795+
typeAnnotation &&
1796+
typeAnnotation.type === 'TSIndexedAccessType' &&
1797+
typeParameter &&
1798+
typeParameter.constraint &&
1799+
typeParameters
1800+
) {
1801+
const constraint = typeParameter.constraint
1802+
if (
1803+
constraint.type === 'TSTypeOperator' &&
1804+
constraint.operator === 'keyof' &&
1805+
constraint.typeAnnotation &&
1806+
constraint.typeAnnotation.type === 'TSTypeReference' &&
1807+
constraint.typeAnnotation.typeName.type === 'Identifier'
1808+
) {
1809+
const typeName = constraint.typeAnnotation.typeName.name
1810+
const index = typeAnnotation.indexType
1811+
const obj = typeAnnotation.objectType
1812+
if (
1813+
obj &&
1814+
obj.type === 'TSTypeReference' &&
1815+
obj.typeName.type === 'Identifier' &&
1816+
obj.typeName.name === typeName &&
1817+
index &&
1818+
index.type === 'TSTypeReference' &&
1819+
index.typeName.type === 'Identifier' &&
1820+
index.typeName.name === typeParameter.name
1821+
) {
1822+
const targetType = typeParameters[typeName]
1823+
if (targetType) {
1824+
return inferRuntimeType(ctx, targetType, scope)
1825+
}
1826+
}
1827+
}
1828+
}
1829+
1830+
return [UNKNOWN_TYPE]
1831+
}
17841832

17851833
case 'TSEnumDeclaration':
17861834
return inferEnumType(node)

0 commit comments

Comments
 (0)