Skip to content

Commit 7def8b1

Browse files
authored
feat(complier-sfc): hoist literal constants for script (#5752)
- Support using literal constants in macros - fix useCssVars insert position edge case - fix non-literal-const enum hoisting close #5750
1 parent e224922 commit 7def8b1

File tree

10 files changed

+466
-61
lines changed

10 files changed

+466
-61
lines changed

packages/compiler-core/src/options.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,11 @@ export const enum BindingTypes {
112112
/**
113113
* declared by other options, e.g. computed, inject
114114
*/
115-
OPTIONS = 'options'
115+
OPTIONS = 'options',
116+
/**
117+
* a literal constant, e.g. 'foo', 1, true
118+
*/
119+
LITERAL_CONST = 'literal-const'
116120
}
117121

118122
export type BindingMetadata = {

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -361,7 +361,8 @@ function resolveSetupReference(name: string, context: TransformContext) {
361361

362362
const fromConst =
363363
checkType(BindingTypes.SETUP_CONST) ||
364-
checkType(BindingTypes.SETUP_REACTIVE_CONST)
364+
checkType(BindingTypes.SETUP_REACTIVE_CONST) ||
365+
checkType(BindingTypes.LITERAL_CONST)
365366
if (fromConst) {
366367
return context.inline
367368
? // in inline mode, const setup bindings (e.g. imports) can be used as-is

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

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -128,11 +128,7 @@ export function processExpression(
128128
const isDestructureAssignment =
129129
parent && isInDestructureAssignment(parent, parentStack)
130130

131-
if (
132-
type === BindingTypes.SETUP_CONST ||
133-
type === BindingTypes.SETUP_REACTIVE_CONST ||
134-
localVars[raw]
135-
) {
131+
if (isConst(type) || localVars[raw]) {
136132
return raw
137133
} else if (type === BindingTypes.SETUP_REF) {
138134
return `${raw}.value`
@@ -223,7 +219,7 @@ export function processExpression(
223219
if (!asParams && !isScopeVarReference && !isAllowedGlobal && !isLiteral) {
224220
// const bindings exposed from setup can be skipped for patching but
225221
// cannot be hoisted to module scope
226-
if (bindingMetadata[node.content] === BindingTypes.SETUP_CONST) {
222+
if (isConst(bindingMetadata[node.content])) {
227223
node.constType = ConstantTypes.CAN_SKIP_PATCH
228224
}
229225
node.content = rewriteIdentifier(rawExp)
@@ -372,3 +368,11 @@ export function stringifyExpression(exp: ExpressionNode | string): string {
372368
.join('')
373369
}
374370
}
371+
372+
function isConst(type: unknown) {
373+
return (
374+
type === BindingTypes.SETUP_CONST ||
375+
type === BindingTypes.LITERAL_CONST ||
376+
type === BindingTypes.SETUP_REACTIVE_CONST
377+
)
378+
}

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

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
22

33
exports[`SFC analyze <script> bindings > auto name inference > basic 1`] = `
4-
"export default {
4+
"const a = 1
5+
export default {
56
__name: 'FooBar',
67
setup(__props, { expose }) {
78
expose();
8-
const a = 1
9+
910
return { a }
1011
}
1112

@@ -683,7 +684,9 @@ return { props, get x() { return x } }
683684
`;
684685

685686
exports[`SFC compile <script setup> > defineProps() 1`] = `
686-
"export default {
687+
"const bar = 1
688+
689+
export default {
687690
props: {
688691
foo: String
689692
},
@@ -693,7 +696,6 @@ exports[`SFC compile <script setup> > defineProps() 1`] = `
693696
const props = __props;
694697

695698

696-
const bar = 1
697699

698700
return { props, bar }
699701
}
@@ -755,12 +757,12 @@ return { a, props, emit }
755757
exports[`SFC compile <script setup> > dev mode import usage check > TS annotations 1`] = `
756758
"import { defineComponent as _defineComponent } from 'vue'
757759
import { Foo, Bar, Baz, Qux, Fred } from './x'
760+
const a = 1
758761

759762
export default /*#__PURE__*/_defineComponent({
760763
setup(__props, { expose }) {
761764
expose();
762765

763-
const a = 1
764766
function b() {}
765767

766768
return { a, b, get Baz() { return Baz } }
@@ -772,12 +774,12 @@ return { a, b, get Baz() { return Baz } }
772774
exports[`SFC compile <script setup> > dev mode import usage check > attribute expressions 1`] = `
773775
"import { defineComponent as _defineComponent } from 'vue'
774776
import { bar, baz } from './x'
777+
const cond = true
775778

776779
export default /*#__PURE__*/_defineComponent({
777780
setup(__props, { expose }) {
778781
expose();
779782

780-
const cond = true
781783

782784
return { cond, get bar() { return bar }, get baz() { return baz } }
783785
}
@@ -788,12 +790,12 @@ return { cond, get bar() { return bar }, get baz() { return baz } }
788790
exports[`SFC compile <script setup> > dev mode import usage check > components 1`] = `
789791
"import { defineComponent as _defineComponent } from 'vue'
790792
import { FooBar, FooBaz, FooQux, foo } from './x'
793+
const fooBar: FooBar = 1
791794

792795
export default /*#__PURE__*/_defineComponent({
793796
setup(__props, { expose }) {
794797
expose();
795798

796-
const fooBar: FooBar = 1
797799

798800
return { fooBar, get FooBaz() { return FooBaz }, get FooQux() { return FooQux }, get foo() { return foo } }
799801
}
@@ -886,7 +888,9 @@ return { get bar() { return bar } }
886888
`;
887889

888890
exports[`SFC compile <script setup> > errors > should allow defineProps/Emit() referencing scope var 1`] = `
889-
"export default {
891+
"const bar = 1
892+
893+
export default {
890894
props: {
891895
foo: {
892896
default: bar => bar + 1
@@ -898,7 +902,6 @@ exports[`SFC compile <script setup> > errors > should allow defineProps/Emit() r
898902
setup(__props, { expose }) {
899903
expose();
900904

901-
const bar = 1
902905

903906

904907

@@ -1722,8 +1725,7 @@ return { Foo }
17221725

17231726
exports[`SFC compile <script setup> > with TypeScript > runtime Enum in normal script 1`] = `
17241727
"import { defineComponent as _defineComponent } from 'vue'
1725-
enum Foo { A = 123 }
1726-
1728+
17271729
export enum D { D = \\"D\\" }
17281730
const enum C { C = \\"C\\" }
17291731
enum B { B = \\"B\\" }
@@ -1732,6 +1734,7 @@ export default /*#__PURE__*/_defineComponent({
17321734
setup(__props, { expose }) {
17331735
expose();
17341736

1737+
enum Foo { A = 123 }
17351738

17361739
return { D, C, B, Foo }
17371740
}
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
// Vitest Snapshot v1
2+
3+
exports[`sfc hoist static > should enable when only script setup 1`] = `
4+
"const foo = 'bar'
5+
6+
export default {
7+
setup(__props) {
8+
9+
const foo = 'bar'
10+
11+
return () => {}
12+
}
13+
14+
}"
15+
`;
16+
17+
exports[`sfc hoist static > should hoist expressions 1`] = `
18+
"const unary = !false
19+
const binary = 1 + 2
20+
const conditional = 1 ? 2 : 3
21+
const sequence = (1, true, 'foo', 1)
22+
23+
export default {
24+
setup(__props) {
25+
26+
27+
return () => {}
28+
}
29+
30+
}"
31+
`;
32+
33+
exports[`sfc hoist static > should hoist literal value 1`] = `
34+
"const string = 'default value'
35+
const number = 123
36+
const boolean = false
37+
const nil = null
38+
const bigint = 100n
39+
const template = \`str\`
40+
const regex = /.*/g
41+
42+
export default {
43+
setup(__props) {
44+
45+
46+
return () => {}
47+
}
48+
49+
}"
50+
`;
51+
52+
exports[`sfc hoist static > should hoist w/ defineProps/Emits 1`] = `
53+
"const defaultValue = 'default value'
54+
55+
export default {
56+
props: {
57+
foo: {
58+
default: defaultValue
59+
}
60+
},
61+
setup(__props) {
62+
63+
64+
65+
return () => {}
66+
}
67+
68+
}"
69+
`;
70+
71+
exports[`sfc hoist static > should not hoist a constant initialized to a reference value 1`] = `
72+
"import { defineComponent as _defineComponent } from 'vue'
73+
74+
export default /*#__PURE__*/_defineComponent({
75+
setup(__props) {
76+
77+
const KEY1 = Boolean
78+
const KEY2 = [Boolean]
79+
const KEY3 = [getCurrentInstance()]
80+
let i = 0;
81+
const KEY4 = (i++, 'foo')
82+
enum KEY5 {
83+
FOO = 1,
84+
BAR = getCurrentInstance(),
85+
}
86+
const KEY6 = \`template\${i}\`
87+
88+
return () => {}
89+
}
90+
91+
})"
92+
`;
93+
94+
exports[`sfc hoist static > should not hoist a function or class 1`] = `
95+
"export default {
96+
setup(__props) {
97+
98+
const fn = () => {}
99+
function fn2() {}
100+
class Foo {}
101+
102+
return () => {}
103+
}
104+
105+
}"
106+
`;
107+
108+
exports[`sfc hoist static > should not hoist a object or array 1`] = `
109+
"export default {
110+
setup(__props) {
111+
112+
const obj = { foo: 'bar' }
113+
const arr = [1, 2, 3]
114+
115+
return () => {}
116+
}
117+
118+
}"
119+
`;
120+
121+
exports[`sfc hoist static > should not hoist a variable 1`] = `
122+
"export default {
123+
setup(__props) {
124+
125+
let KEY1 = 'default value'
126+
var KEY2 = 123
127+
128+
return () => {}
129+
}
130+
131+
}"
132+
`;

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

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -51,15 +51,15 @@ export default __default__"
5151
5252
exports[`CSS vars injection > codegen > should ignore comments 1`] = `
5353
"import { useCssVars as _useCssVars, unref as _unref } from 'vue'
54-
54+
const color = 'red';const width = 100
5555
export default {
5656
setup(__props, { expose }) {
5757
expose();
5858
5959
_useCssVars(_ctx => ({
6060
\\"xxxxxxxx-width\\": (width)
6161
}))
62-
const color = 'red';const width = 100
62+
6363
return { color, width }
6464
}
6565
@@ -92,15 +92,15 @@ return { get a() { return a }, set a(v) { a = v }, get b() { return b }, set b(v
9292
9393
exports[`CSS vars injection > codegen > w/ <script setup> 1`] = `
9494
"import { useCssVars as _useCssVars, unref as _unref } from 'vue'
95-
95+
const color = 'red'
9696
export default {
9797
setup(__props, { expose }) {
9898
expose();
9999
100100
_useCssVars(_ctx => ({
101101
\\"xxxxxxxx-color\\": (color)
102102
}))
103-
const color = 'red'
103+
104104
return { color }
105105
}
106106
@@ -109,7 +109,8 @@ return { color }
109109
110110
exports[`CSS vars injection > codegen > w/ <script setup> using the same var multiple times 1`] = `
111111
"import { useCssVars as _useCssVars, unref as _unref } from 'vue'
112-
112+
const color = 'red'
113+
113114
export default {
114115
setup(__props, { expose }) {
115116
expose();
@@ -118,7 +119,6 @@ _useCssVars(_ctx => ({
118119
\\"xxxxxxxx-color\\": (color)
119120
}))
120121
121-
const color = 'red'
122122
123123
return { color }
124124
}
@@ -146,6 +146,7 @@ export default __default__"
146146
exports[`CSS vars injection > w/ <script setup> binding analysis 1`] = `
147147
"import { useCssVars as _useCssVars, unref as _unref } from 'vue'
148148
import { ref } from 'vue'
149+
const color = 'red'
149150
150151
export default {
151152
props: {
@@ -160,7 +161,6 @@ _useCssVars(_ctx => ({
160161
\\"xxxxxxxx-foo\\": (__props.foo)
161162
}))
162163
163-
const color = 'red'
164164
const size = ref('10px')
165165
166166

0 commit comments

Comments
 (0)