Skip to content

Commit d803932

Browse files
committed
Move stylex.create to EOF
1 parent 67e55e5 commit d803932

51 files changed

Lines changed: 965 additions & 944 deletions

Some content is hidden

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

scripts/regenerate-outputs.mjs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { transformWithWarnings } from "../dist/transform.mjs";
2+
import { fixtureAdapter } from "../src/__tests__/fixture-adapters.ts";
3+
import fs from "fs";
4+
import path from "path";
5+
import jscodeshift from "jscodeshift";
6+
7+
const j = jscodeshift.withParser("tsx");
8+
9+
const testCasesDir = "test-cases";
10+
const files = fs.readdirSync(testCasesDir);
11+
const inputFiles = files.filter(
12+
(f) =>
13+
f.endsWith(".input.tsx") && !f.startsWith("_unsupported.") && !f.startsWith("unsupported-"),
14+
);
15+
16+
let updated = 0;
17+
for (const inputFile of inputFiles) {
18+
const name = inputFile.replace(".input.tsx", "");
19+
const inputPath = path.join(testCasesDir, inputFile);
20+
const outputPath = path.join(testCasesDir, name + ".output.tsx");
21+
22+
const input = fs.readFileSync(inputPath, "utf-8");
23+
const result = transformWithWarnings(
24+
{ source: input, path: path.resolve(inputPath) },
25+
{ jscodeshift: j, j },
26+
{ adapter: fixtureAdapter },
27+
);
28+
29+
if (result.code) {
30+
fs.writeFileSync(outputPath, result.code);
31+
updated++;
32+
}
33+
}
34+
35+
process.stdout.write("Updated " + updated + " output files\n");

src/__tests__/fixture-adapters.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { defineAdapter } from "../adapter.js";
1+
import { defineAdapter } from "../adapter.ts";
22

33
// Test adapters - examples of custom adapter usage
44
export const customAdapter = defineAdapter({

src/internal/emit-styles.ts

Lines changed: 3 additions & 160 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ export function emitStylesAndImports(args: {
1111
resolverImports: Map<string, any>;
1212
resolvedStyleObjects: Map<string, any>;
1313
styledDecls: StyledDecl[];
14-
cssHelperNames: Set<string>;
1514
isAstNode: (v: unknown) => boolean;
1615
objectToAst: (j: any, v: Record<string, unknown>) => any;
1716
literalToAst: (j: any, v: unknown) => any;
@@ -24,7 +23,6 @@ export function emitStylesAndImports(args: {
2423
resolverImports,
2524
resolvedStyleObjects,
2625
styledDecls,
27-
cssHelperNames,
2826
isAstNode,
2927
objectToAst,
3028
literalToAst,
@@ -513,163 +511,8 @@ export function emitStylesAndImports(args: {
513511
(stylesDecl as any).comments = deduped;
514512
}
515513

516-
// If styles reference identifiers declared later in the file (e.g. string-interpolation fixture),
517-
// insert `styles` after the last such declaration to satisfy StyleX evaluation order.
518-
const referencedIdents = new Set<string>();
519-
{
520-
const seen = new WeakSet<object>();
521-
const visit = (cur: any) => {
522-
if (!cur) {
523-
return;
524-
}
525-
if (Array.isArray(cur)) {
526-
for (const c of cur) {
527-
visit(c);
528-
}
529-
return;
530-
}
531-
if (typeof cur !== "object") {
532-
return;
533-
}
534-
if (seen.has(cur as object)) {
535-
return;
536-
}
537-
seen.add(cur as object);
538-
if (cur.type === "Identifier" && typeof cur.name === "string") {
539-
referencedIdents.add(cur.name);
540-
}
541-
for (const v of Object.values(cur)) {
542-
if (typeof v === "object") {
543-
visit(v);
544-
}
545-
}
546-
};
547-
for (const v of resolvedStyleObjects.values()) {
548-
if (isAstNode(v)) {
549-
visit(v);
550-
} else if (v && typeof v === "object") {
551-
visit(objectToAst(j, v as any));
552-
}
553-
}
554-
}
555-
514+
// Place `styles` at the very end of the file.
515+
// This keeps component logic first, styles last for better readability.
556516
const programBody = root.get().node.program.body as any[];
557-
const declsRefIdx = (() => {
558-
let last = -1;
559-
for (let i = 0; i < programBody.length; i++) {
560-
const stmt = programBody[i];
561-
if (!stmt) {
562-
continue;
563-
}
564-
if (stmt.type === "VariableDeclaration") {
565-
for (const d of stmt.declarations ?? []) {
566-
const id = d?.id;
567-
if (id?.type === "Identifier" && referencedIdents.has(id.name)) {
568-
last = i;
569-
}
570-
}
571-
} else if (stmt.type === "FunctionDeclaration") {
572-
const id = stmt.id;
573-
if (id?.type === "Identifier" && referencedIdents.has(id.name)) {
574-
last = i;
575-
}
576-
}
577-
}
578-
return last >= 0 ? last : null;
579-
})();
580-
581-
// Try to place `styles` where the first styled component declaration used to be:
582-
// insert right before the earliest styled decl statement (i.e. after the statement before it).
583-
const firstStyledDeclInsertionAfterIdx = (() => {
584-
if (!styledDecls.length) {
585-
return null;
586-
}
587-
const styledLocalNames = new Set(styledDecls.map((d) => d.localName));
588-
let firstIdx: number | null = null;
589-
for (let i = 0; i < programBody.length; i++) {
590-
const stmt = programBody[i];
591-
if (stmt?.type !== "VariableDeclaration") {
592-
continue;
593-
}
594-
for (const d of stmt.declarations ?? []) {
595-
const id = d?.id;
596-
if (id?.type !== "Identifier") {
597-
continue;
598-
}
599-
if (!styledLocalNames.has(id.name)) {
600-
continue;
601-
}
602-
firstIdx = firstIdx === null ? i : Math.min(firstIdx, i);
603-
}
604-
}
605-
return firstIdx === null ? null : firstIdx - 1;
606-
})();
607-
608-
const lastImportIdx = (() => {
609-
let last = -1;
610-
for (let i = 0; i < programBody.length; i++) {
611-
if (programBody[i]?.type === "ImportDeclaration") {
612-
last = i;
613-
}
614-
}
615-
return last;
616-
})();
617-
618-
const lastKeyframesIdx = (() => {
619-
let last = -1;
620-
for (let i = 0; i < programBody.length; i++) {
621-
const stmt = programBody[i];
622-
if (stmt?.type !== "VariableDeclaration") {
623-
continue;
624-
}
625-
for (const d of stmt.declarations ?? []) {
626-
const init: any = d?.init;
627-
if (
628-
init &&
629-
init.type === "CallExpression" &&
630-
init.callee?.type === "MemberExpression" &&
631-
init.callee.object?.type === "Identifier" &&
632-
init.callee.object.name === "stylex" &&
633-
init.callee.property?.type === "Identifier" &&
634-
init.callee.property.name === "keyframes"
635-
) {
636-
last = i;
637-
}
638-
}
639-
}
640-
return last;
641-
})();
642-
643-
const lastCssHelperIdx = (() => {
644-
let last = -1;
645-
for (let i = 0; i < programBody.length; i++) {
646-
const stmt = programBody[i];
647-
if (stmt?.type !== "VariableDeclaration") {
648-
continue;
649-
}
650-
for (const d of stmt.declarations ?? []) {
651-
const id: any = d?.id;
652-
if (id?.type === "Identifier" && cssHelperNames.has(id.name)) {
653-
last = i;
654-
}
655-
}
656-
}
657-
return last;
658-
})();
659-
660-
// Pick the latest safe insertion point: after imports, after any keyframes/css helpers,
661-
// after any referenced identifier declarations, and after the original styled-decl anchor.
662-
const insertAfterIdx = Math.max(
663-
lastImportIdx,
664-
lastKeyframesIdx,
665-
lastCssHelperIdx,
666-
declsRefIdx ?? -1,
667-
firstStyledDeclInsertionAfterIdx ?? -1,
668-
);
669-
670-
if (insertAfterIdx >= 0) {
671-
programBody.splice(insertAfterIdx + 1, 0, stylesDecl as any);
672-
} else {
673-
programBody.unshift(stylesDecl as any);
674-
}
517+
programBody.push(stylesDecl as any);
675518
}

src/internal/emit-wrappers.ts

Lines changed: 79 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2168,15 +2168,85 @@ export function emitWrappers(args: {
21682168
return exportNode;
21692169
});
21702170

2171-
root
2172-
.find(j.VariableDeclaration)
2173-
.filter((p: any) =>
2174-
p.node.declarations.some(
2175-
(dcl: any) => dcl.type === "VariableDeclarator" && (dcl.id as any)?.name === "styles",
2176-
),
2177-
)
2178-
.at(0)
2179-
.insertAfter(wrappedOrdered);
2171+
// Replace each styled declaration in-place with its wrapper function.
2172+
// This preserves the original position of components in the file.
2173+
for (const d of wrapperDecls) {
2174+
const wrapperNodes = wrappedOrdered.filter((node: any) => {
2175+
if (node?.type === "FunctionDeclaration") {
2176+
return node.id?.name === d.localName;
2177+
}
2178+
if (node?.type === "ExportNamedDeclaration" || node?.type === "ExportDefaultDeclaration") {
2179+
const decl = node.declaration;
2180+
return decl?.type === "FunctionDeclaration" && decl.id?.name === d.localName;
2181+
}
2182+
if (node?.type === "TSTypeAliasDeclaration") {
2183+
const name = node.id?.name;
2184+
return name === `${d.localName}Props`;
2185+
}
2186+
return false;
2187+
});
2188+
2189+
if (wrapperNodes.length === 0) {
2190+
continue;
2191+
}
2192+
2193+
// Find the original styled declaration
2194+
const styledDecl = root
2195+
.find(j.VariableDeclaration)
2196+
.filter((p: any) =>
2197+
p.node.declarations.some(
2198+
(dcl: any) =>
2199+
dcl.type === "VariableDeclarator" &&
2200+
dcl.id?.type === "Identifier" &&
2201+
dcl.id.name === d.localName,
2202+
),
2203+
);
2204+
2205+
if (styledDecl.size() > 0) {
2206+
// Check if it's inside an export declaration
2207+
const firstPath = styledDecl.paths()[0];
2208+
const parent = firstPath?.parentPath;
2209+
if (parent && parent.node?.type === "ExportNamedDeclaration") {
2210+
// Replace the export declaration
2211+
j(parent).replaceWith(wrapperNodes);
2212+
} else {
2213+
// Replace the variable declaration
2214+
styledDecl.at(0).replaceWith(wrapperNodes);
2215+
}
2216+
}
2217+
}
2218+
2219+
// Insert any remaining nodes (types not associated with a specific wrapper) before styles
2220+
const insertedNames = new Set(wrapperDecls.map((d) => d.localName));
2221+
const remainingNodes = wrappedOrdered.filter((node: any) => {
2222+
if (node?.type === "FunctionDeclaration") {
2223+
return !insertedNames.has(node.id?.name);
2224+
}
2225+
if (node?.type === "ExportNamedDeclaration" || node?.type === "ExportDefaultDeclaration") {
2226+
const decl = node.declaration;
2227+
return !(decl?.type === "FunctionDeclaration" && insertedNames.has(decl.id?.name));
2228+
}
2229+
if (node?.type === "TSTypeAliasDeclaration") {
2230+
const name = node.id?.name;
2231+
if (name?.endsWith("Props")) {
2232+
const base = name.slice(0, -5);
2233+
return !insertedNames.has(base);
2234+
}
2235+
}
2236+
return true;
2237+
});
2238+
2239+
if (remainingNodes.length > 0) {
2240+
root
2241+
.find(j.VariableDeclaration)
2242+
.filter((p: any) =>
2243+
p.node.declarations.some(
2244+
(dcl: any) => dcl.type === "VariableDeclarator" && (dcl.id as any)?.name === "styles",
2245+
),
2246+
)
2247+
.at(0)
2248+
.insertBefore(remainingNodes);
2249+
}
21802250
}
21812251

21822252
if (emitTypes && needsReactTypeImport) {

0 commit comments

Comments
 (0)