Skip to content

Commit 512c91f

Browse files
sjchmielaAustaras
andauthored
fix(es/minifier): Fix minification of framer-motion by checking cons.termniates() (#9818)
**Description:** Next.js 15 seems to minify parts of `framer-motion`'s code in an invalid way, changing the meaning. The original function modifies the single `definition` variable as it changes meaning (see `input.js`): - if `definition` is a function, it calls it and saves the return value as `definition` - if `definition` is a string, it resolves animation variant based on value as name and saves the variant as `definition` - if `definition` is again a function, it calls it again and saves the return value as `definition` again However, `swc` seems to be not noticing that `definition` may be mutated between the calls and collapses all `if`s into one: ``` // `resolveVariantFromProps` in framer-motion // https://github.com/motiondivision/motion/blob/ecd97f7dce8954be300aa73ab6a96208437941c5/packages/framer-motion/src/render/utils/resolve-variants.ts#L31-L72 export function resolveVariantFromProps(props, definition, custom, visualElement) { if ("function" == typeof definition || ("string" == typeof definition && (definition = props.variants && props.variants[definition]), "function" == typeof definition)) { const [current, velocity] = getValueState(visualElement); definition = definition(void 0 !== custom ? custom : props.custom, current, velocity); } return definition; } ``` which skips the first call to `definition`, first "resolution" step. Note: I suspect the `output.js` may not be precisely what SWC may output as minified code (maybe there is a way to minify it further). It definitely differs from the current output though by the initial call and reassignment to `definition`. --------- Co-authored-by: austaras <austaras@outlook.com>
1 parent 809bff6 commit 512c91f

File tree

4 files changed

+50
-1
lines changed

4 files changed

+50
-1
lines changed

Diff for: .changeset/gold-shoes-cross.md

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
swc_core: patch
3+
swc_ecma_minifier: patch
4+
---
5+
6+
fix(es/minifier): Add failing test case for an example in `framer-motion`

Diff for: crates/swc_ecma_minifier/src/compress/optimize/conditionals.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,9 @@ impl Optimizer<'_> {
121121
(
122122
Some(Stmt::If(l @ IfStmt { alt: None, .. })),
123123
Some(Stmt::If(r @ IfStmt { alt: None, .. })),
124-
) => SyntaxContext::within_ignored_ctxt(|| l.cons.eq_ignore_span(&r.cons)),
124+
) => SyntaxContext::within_ignored_ctxt(|| {
125+
l.cons.eq_ignore_span(&r.cons) && l.cons.terminates()
126+
}),
125127
_ => false,
126128
});
127129
if !has_work {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// `resolveVariantFromProps` in framer-motion
2+
// https://github.com/motiondivision/motion/blob/ecd97f7dce8954be300aa73ab6a96208437941c5/packages/framer-motion/src/render/utils/resolve-variants.ts#L31-L72
3+
4+
export function resolveVariantFromProps(props, definition, custom, visualElement) {
5+
if (typeof definition === "function") {
6+
const [current, velocity] = getValueState(visualElement)
7+
definition = definition(
8+
custom !== undefined ? custom : props.custom,
9+
current,
10+
velocity
11+
)
12+
}
13+
14+
if (typeof definition === "string") {
15+
definition = props.variants && props.variants[definition]
16+
}
17+
18+
if (typeof definition === "function") {
19+
const [current, velocity] = getValueState(visualElement)
20+
definition = definition(
21+
custom !== undefined ? custom : props.custom,
22+
current,
23+
velocity
24+
)
25+
}
26+
27+
return definition
28+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// `resolveVariantFromProps` in framer-motion
2+
// https://github.com/motiondivision/motion/blob/ecd97f7dce8954be300aa73ab6a96208437941c5/packages/framer-motion/src/render/utils/resolve-variants.ts#L31-L72
3+
export function resolveVariantFromProps(props, definition, custom, visualElement) {
4+
if ("function" == typeof definition) {
5+
const [current, velocity] = getValueState(visualElement);
6+
definition = definition(void 0 !== custom ? custom : props.custom, current, velocity);
7+
}
8+
if ("string" == typeof definition && (definition = props.variants && props.variants[definition]), "function" == typeof definition) {
9+
const [current, velocity] = getValueState(visualElement);
10+
definition = definition(void 0 !== custom ? custom : props.custom, current, velocity);
11+
}
12+
return definition;
13+
}

0 commit comments

Comments
 (0)