Skip to content

Commit c00a579

Browse files
authored
fix(define-stylex): prevent unintended code hoisting(#987)
1 parent ea31e15 commit c00a579

File tree

4 files changed

+92
-17
lines changed

4 files changed

+92
-17
lines changed

packages/define-stylex/src/core/index.ts

Lines changed: 20 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import {
1515
type NodeTransform,
1616
type NodeTypes,
1717
} from '@vue/compiler-dom'
18-
import type { Node } from '@babel/types'
18+
import type { CallExpression, Node } from '@babel/types'
1919

2020
const STYLEX_CREATE = '_stylex_create'
2121
const STYLEX_PROPS = '_stylex_props'
@@ -67,33 +67,36 @@ export function transformDefineStyleX(
6767
const { scriptSetup, getSetupAst, template } = sfc
6868
if (!scriptSetup || !template) return
6969

70-
let scriptOffset: number | undefined
7170
const setupOffset = scriptSetup.loc.start.offset
7271

7372
const s = new MagicStringAST(code)
7473
const normalScript = addNormalScript(sfc, s)
75-
76-
function moveToScript(decl: Node, prefix: 'const ' | '' = '') {
77-
if (scriptOffset === undefined) scriptOffset = normalScript.start()
78-
79-
const text = `\n${prefix}${s.sliceNode(decl, { offset: setupOffset })}`
80-
s.appendRight(scriptOffset, text)
81-
82-
s.removeNode(decl, { offset: setupOffset })
83-
}
74+
const scriptOffset = normalScript.start()
8475

8576
const setupAST = getSetupAst()!
8677

8778
walkAST<Node>(setupAST, {
8879
enter(node) {
8980
if (node.type !== 'VariableDeclaration') return
81+
const shouldChange = node.declarations.some((decl) =>
82+
isCallOf(decl.init, DEFINE_STYLEX),
83+
)
84+
if (!shouldChange) return
85+
9086
node.declarations.forEach((decl) => {
91-
if (!isCallOf(decl.init, DEFINE_STYLEX)) return
92-
s.overwriteNode(decl.init.callee, STYLEX_CREATE, {
93-
offset: setupOffset,
94-
})
87+
const isDefineStyleX = isCallOf(decl.init, DEFINE_STYLEX)
88+
if (isDefineStyleX) {
89+
s.overwriteNode((decl.init as CallExpression).callee, STYLEX_CREATE, {
90+
offset: setupOffset,
91+
})
92+
}
93+
const text = `\n${node.kind} ${s.sliceNode(decl, { offset: setupOffset })}`
94+
s.appendRight(
95+
isDefineStyleX ? scriptOffset : node.start! + setupOffset - 1,
96+
text,
97+
)
9598
})
96-
moveToScript(node)
99+
s.removeNode(node, { offset: setupOffset })
97100
},
98101
})
99102

@@ -104,7 +107,7 @@ export function transformDefineStyleX(
104107
})
105108
traverseNode(template.ast!, ctx)
106109

107-
s.appendRight(
110+
s.appendLeft(
108111
setupOffset,
109112
`\nimport { create as ${STYLEX_CREATE}, props as ${STYLEX_PROPS} } from '@stylexjs/stylex'`,
110113
)

packages/define-stylex/tests/__snapshots__/fixtures.test.ts.snap

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,26 @@ import { create as _stylex_create, props as _stylex_props } from '@stylexjs/styl
3333
"
3434
`;
3535

36+
exports[`fixtures > ./fixtures/multiple-statements.vue 1`] = `
37+
"<script lang="ts">
38+
const stylesA = _stylex_create({ redBold: { color: 'red', fontWeight: 'bold' } })
39+
const stylesB = _stylex_create({ redBold: { color: 'red', fontWeight: 'bold' } })
40+
</script>
41+
<script setup lang="ts">
42+
import { create as _stylex_create, props as _stylex_props } from '@stylexjs/stylex'
43+
const a = 'a'
44+
45+
</script>
46+
47+
<template>
48+
<p>
49+
<span v-bind="_stylex_props(stylesA.redBold)">Red</span>
50+
<span v-bind="_stylex_props(stylesB.redBold)">{{ a }}</span>
51+
</p>
52+
</template>
53+
"
54+
`;
55+
3656
exports[`fixtures > ./fixtures/optional-rules.vue 1`] = `
3757
"<script lang="ts">
3858
const styles = _stylex_create({
@@ -51,3 +71,27 @@ defineProps<{ bold?: boolean }>()
5171
</template>
5272
"
5373
`;
74+
75+
exports[`fixtures > ./fixtures/other-statements.vue 1`] = `
76+
"<script lang="ts">
77+
const styles = _stylex_create({ redBold: { color: 'red', fontWeight: 'bold' } })
78+
</script>
79+
<script setup lang="ts">
80+
import { create as _stylex_create, props as _stylex_props } from '@stylexjs/stylex'
81+
// separated declarations before
82+
const a = 'a'
83+
// multiple declarations
84+
const b = 'b'
85+
const c = 'c'
86+
87+
// separated declarations after
88+
const d = 'd'
89+
</script>
90+
91+
<template>
92+
<p>
93+
<span v-bind="_stylex_props(styles.redBold)">Red</span> {{ a }} {{ b }} {{ c }} {{ d }}
94+
</p>
95+
</template>
96+
"
97+
`;
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<script setup lang="ts">
2+
const a = 'a'
3+
const stylesA = defineStyleX({ redBold: { color: 'red', fontWeight: 'bold' } }),
4+
stylesB = defineStyleX({ redBold: { color: 'red', fontWeight: 'bold' } })
5+
</script>
6+
7+
<template>
8+
<p>
9+
<span v-stylex="stylesA.redBold">Red</span>
10+
<span v-stylex="stylesB.redBold">{{ a }}</span>
11+
</p>
12+
</template>
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<script setup lang="ts">
2+
// separated declarations before
3+
const a = 'a'
4+
// multiple declarations
5+
const b = 'b',
6+
styles = defineStyleX({ redBold: { color: 'red', fontWeight: 'bold' } }),
7+
c = 'c'
8+
// separated declarations after
9+
const d = 'd'
10+
</script>
11+
12+
<template>
13+
<p>
14+
<span v-stylex="styles.redBold">Red</span> {{ a }} {{ b }} {{ c }} {{ d }}
15+
</p>
16+
</template>

0 commit comments

Comments
 (0)