Skip to content

Commit a46ca8f

Browse files
committed
fix: remove props destructure blank line formatting rule
The blanket removal of blank lines after `} = props;` was too aggressive — it stripped intentional blank lines (e.g. in selector-dynamicPseudoElement). Remove the formatting rule entirely and regenerate all test outputs to match recast's actual behavior. https://claude.ai/code/session_01FfBbr11ihPBfMeuo3r2yX3
1 parent 35c8d62 commit a46ca8f

File tree

194 files changed

+275
-192
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

194 files changed

+275
-192
lines changed

src/__tests__/format-output.test.ts

Lines changed: 0 additions & 86 deletions
This file was deleted.

src/internal/utilities/format-output.ts

Lines changed: 0 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -158,112 +158,6 @@ export function formatOutput(code: string): string {
158158
return lines.join("\n");
159159
})();
160160

161-
// Remove blank line between props destructure and the next statement.
162-
// recast inserts a blank line after `const { ... } = props;` inside function bodies.
163-
// We use a line-based approach to avoid matching inside string/template literals.
164-
out = removeBlankLineAfterPropsDestructure(out);
165-
166161
// Normalize EOF: trim all trailing whitespace, then ensure a single trailing newline.
167162
return out.trimEnd() + "\n";
168163
}
169-
170-
/**
171-
* Remove blank lines after `} = props;` destructuring statements in function bodies.
172-
* Uses line-by-line analysis to avoid modifying matching patterns inside
173-
* string/template literals or comments.
174-
*/
175-
function removeBlankLineAfterPropsDestructure(code: string): string {
176-
const lines = code.split("\n");
177-
const result: string[] = [];
178-
let inString = false;
179-
let stringChar = "";
180-
let inBlockComment = false;
181-
let inTemplateLiteral = false;
182-
183-
for (let i = 0; i < lines.length; i++) {
184-
const line = lines[i] ?? "";
185-
186-
// Track whether we're inside a multi-line string/comment context.
187-
// If so, just pass the line through unchanged.
188-
if (inBlockComment) {
189-
result.push(line);
190-
if (line.includes("*/")) {
191-
inBlockComment = false;
192-
}
193-
continue;
194-
}
195-
if (inTemplateLiteral) {
196-
result.push(line);
197-
// Count unescaped backticks to detect end of template literal
198-
for (let c = 0; c < line.length; c++) {
199-
if (line[c] === "\\") {
200-
c++; // skip escaped char
201-
} else if (line[c] === "`") {
202-
inTemplateLiteral = false;
203-
}
204-
}
205-
continue;
206-
}
207-
if (inString) {
208-
result.push(line);
209-
// Multi-line strings (only template literals can truly span lines,
210-
// but handle edge cases)
211-
if (line.includes(stringChar)) {
212-
inString = false;
213-
}
214-
continue;
215-
}
216-
217-
// Check if this line ends with `} = props;` (the destructuring pattern)
218-
// and the next line is blank — if so, skip the blank line.
219-
const trimmed = line.trimEnd();
220-
if (
221-
trimmed.endsWith("} = props;") &&
222-
i + 1 < lines.length &&
223-
(lines[i + 1] ?? "").trim() === ""
224-
) {
225-
result.push(line);
226-
i++; // skip the blank line
227-
continue;
228-
}
229-
230-
result.push(line);
231-
232-
// Update string/comment tracking for subsequent lines
233-
// Simple heuristic: scan the line for unmatched quotes/comments
234-
for (let c = 0; c < line.length; c++) {
235-
const ch = line[c];
236-
if (ch === "/" && line[c + 1] === "/") {
237-
break; // rest of line is a comment
238-
}
239-
if (ch === "/" && line[c + 1] === "*") {
240-
if (!line.includes("*/", c + 2)) {
241-
inBlockComment = true;
242-
}
243-
break;
244-
}
245-
if (ch === "\\") {
246-
c++; // skip escaped char
247-
continue;
248-
}
249-
if (ch === "`") {
250-
inTemplateLiteral = true;
251-
// Check if it closes on the same line
252-
for (let j = c + 1; j < line.length; j++) {
253-
if (line[j] === "\\") {
254-
j++;
255-
} else if (line[j] === "`") {
256-
inTemplateLiteral = false;
257-
c = j;
258-
break;
259-
}
260-
}
261-
if (inTemplateLiteral) {
262-
break;
263-
}
264-
}
265-
}
266-
}
267-
268-
return result.join("\n");
269-
}

test-cases/asProp-basic.output.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ function Button<C extends React.ElementType = "button">(
77
props: Omit<React.ComponentPropsWithRef<C>, "className" | "style"> & { as?: C },
88
) {
99
const { as: Component = "button", children, ...rest } = props;
10+
1011
return (
1112
<Component {...rest} {...stylex.props(styles.button)}>
1213
{children}
@@ -28,6 +29,7 @@ type StyledTextProps<C extends React.ElementType = typeof Text> = React.Componen
2829
// When used with as="label", the component's props must be preserved
2930
function StyledText<C extends React.ElementType = typeof Text>(props: StyledTextProps<C>) {
3031
const { as: Component = Text, ...rest } = props;
32+
3133
return <Component {...rest} {...stylex.props(styles.text)} />;
3234
}
3335

test-cases/asProp-componentRef.output.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ function AnimatedText<C extends React.ElementType = "span">(
2828
props: Omit<React.ComponentPropsWithRef<C>, "className"> & { as?: C },
2929
) {
3030
const { as: Component = "span", children, style, ...rest } = props;
31+
3132
return (
3233
<Component {...rest} {...mergedSx(styles.animatedText, undefined, style)}>
3334
{children}

test-cases/asProp-crossFile.output.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ type HeaderTitleProps<C extends React.ElementType = typeof Text> = React.Compone
1414

1515
export function HeaderTitle<C extends React.ElementType = typeof Text>(props: HeaderTitleProps<C>) {
1616
const { as: Component = Text, ...rest } = props;
17+
1718
return <Component {...rest} {...stylex.props(styles.headerTitle)} />;
1819
}
1920

test-cases/asProp-exported.output.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ export function ContentViewContainer<C extends React.ElementType = "div">(
55
props: Omit<React.ComponentPropsWithRef<C>, "className" | "style"> & { as?: C },
66
) {
77
const { as: Component = "div", children, ...rest } = props;
8+
89
return (
910
<Component {...rest} {...stylex.props(styles.contentViewContainer)}>
1011
{children}
@@ -37,6 +38,7 @@ export function StyledWrapper<C extends React.ElementType = typeof BaseComponent
3738
props: StyledWrapperProps<C>,
3839
) {
3940
const { as: Component = BaseComponent, variant, ...rest } = props;
41+
4042
return (
4143
<Component
4244
{...rest}

test-cases/asProp-forwarded.output.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ function Button<C extends React.ElementType = "button">(
99
} & { forwardedAs?: React.ElementType },
1010
) {
1111
const { as: Component = "button", forwardedAs, className, children, style, sx, ...rest } = props;
12+
1213
return (
1314
<Component {...rest} as={forwardedAs} {...mergedSx([styles.button, sx], className, style)}>
1415
{children}

test-cases/asProp-usage.output.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ function FullWidthCopyText<C extends React.ElementType = "div">(
1313
props: Omit<React.ComponentPropsWithRef<C>, "className" | "style"> & { as?: C },
1414
) {
1515
const { as: Component = "div", children, ...rest } = props;
16+
1617
return (
1718
<Component {...rest} {...stylex.props(styles.fullWidthCopyText)}>
1819
{children}

test-cases/attrs-asRefType.output.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ function Text<C extends React.ElementType = "span">(
1919
variant = "regular",
2020
...rest
2121
} = props;
22+
2223
return (
2324
<Component {...rest} {...mergedSx([styles.text, variants[variant], sx], className, style)}>
2425
{children}
@@ -36,6 +37,7 @@ type LabelProps = {
3637
// 2. ref with type RefObject<HTMLLabelElement>
3738
function Label(props: LabelProps) {
3839
const { children, ref, ...rest } = props;
40+
3941
return (
4042
<Text ref={ref} {...rest} as="label" {...stylex.props(styles.label)}>
4143
{children}

test-cases/attrs-chained.output.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ type StyledButtonProps = { ref?: React.Ref<HTMLButtonElement> } & React.Componen
2323
// B's wrapper semantics (as="button") would be lost.
2424
function StyledButton(props: StyledButtonProps) {
2525
const { className, style, sx, ...rest } = props;
26+
2627
return <Text {...rest} as="button" {...mergedSx([styles.button, sx], className, style)} />;
2728
}
2829

0 commit comments

Comments
 (0)