Skip to content

Commit f80b251

Browse files
authored
Add an "all" mode to the react transform (#421)
"All" mode transforms all components to be reactive to signals. You can still opt-out using `/** @notrackSignals */`. Related: #412
1 parent ad6305c commit f80b251

File tree

3 files changed

+131
-1
lines changed

3 files changed

+131
-1
lines changed

.changeset/proud-dogs-study.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@preact/signals-react-transform": patch
3+
---
4+
5+
Add an "all" mode to the react transform that transforms all components to be reactive to signals

packages/react-transform/src/index.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,10 @@ function shouldTransform(
160160
// Opt-in opts in to transformation regardless of mode
161161
if (isOptedIntoSignalTracking(path)) return true;
162162

163+
if (options.mode === "all") {
164+
return isComponentFunction(path);
165+
}
166+
163167
if (options.mode == null || options.mode === "auto") {
164168
return (isComponentFunction(path) || isCustomHook(path))
165169
&& getData(path.scope, maybeUsesSignal) === true // Function appears to use signals;
@@ -311,8 +315,9 @@ export interface PluginOptions {
311315
* Specify the mode to use:
312316
* - `auto`: Automatically wrap all components that use signals.
313317
* - `manual`: Only wrap components that are annotated with `@trackSignals` in a JSX comment.
318+
* - `all`: Makes all components reactive to signals.
314319
*/
315-
mode?: "auto" | "manual";
320+
mode?: "auto" | "manual" | "all";
316321
/** Specify a custom package to import the `useSignals` hook from. */
317322
importSource?: string;
318323
experimental?: {

packages/react-transform/test/node/index.test.tsx

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -834,6 +834,126 @@ describe("React Signals Babel Transform", () => {
834834
});
835835
});
836836

837+
describe("all mode transformations", () => {
838+
it("skips transforming arrow function component with leading opt-out JSDoc comment before variable declaration", () => {
839+
const inputCode = `
840+
/** @noTrackSignals */
841+
const MyComponent = () => {
842+
return <div>{signal.value}</div>;
843+
};
844+
`;
845+
846+
const expectedOutput = inputCode;
847+
848+
runTest(inputCode, expectedOutput, { mode: "all" });
849+
});
850+
851+
it("skips transforming function declaration components with leading opt-out JSDoc comment", () => {
852+
const inputCode = `
853+
/** @noTrackSignals */
854+
function MyComponent() {
855+
return <div>{signal.value}</div>;
856+
}
857+
`;
858+
859+
const expectedOutput = inputCode;
860+
861+
runTest(inputCode, expectedOutput, { mode: "all" });
862+
});
863+
864+
it("transforms function declaration component that doesn't use signals", () => {
865+
const inputCode = `
866+
function MyComponent() {
867+
return <div>Hello World</div>;
868+
}
869+
`;
870+
871+
const expectedOutput = `
872+
import { useSignals as _useSignals } from "@preact/signals-react/runtime";
873+
function MyComponent() {
874+
var _effect = _useSignals();
875+
try {
876+
return <div>Hello World</div>;
877+
} finally {
878+
_effect.f();
879+
}
880+
}
881+
`;
882+
883+
runTest(inputCode, expectedOutput, { mode: "all" });
884+
});
885+
886+
it("transforms arrow function component with return statement that doesn't use signals", () => {
887+
const inputCode = `
888+
const MyComponent = () => {
889+
return <div>Hello World</div>;
890+
};
891+
`;
892+
893+
const expectedOutput = `
894+
import { useSignals as _useSignals } from "@preact/signals-react/runtime";
895+
const MyComponent = () => {
896+
var _effect = _useSignals();
897+
try {
898+
return <div>Hello World</div>;
899+
} finally {
900+
_effect.f();
901+
}
902+
};
903+
`;
904+
905+
runTest(inputCode, expectedOutput, { mode: "all" });
906+
});
907+
908+
it("transforms function declaration component that uses signals", () => {
909+
const inputCode = `
910+
function MyComponent() {
911+
signal.value;
912+
return <div>Hello World</div>;
913+
}
914+
`;
915+
916+
const expectedOutput = `
917+
import { useSignals as _useSignals } from "@preact/signals-react/runtime";
918+
function MyComponent() {
919+
var _effect = _useSignals();
920+
try {
921+
signal.value;
922+
return <div>Hello World</div>;
923+
} finally {
924+
_effect.f();
925+
}
926+
}
927+
`;
928+
929+
runTest(inputCode, expectedOutput, { mode: "all" });
930+
});
931+
932+
it("transforms arrow function component with return statement that uses signals", () => {
933+
const inputCode = `
934+
const MyComponent = () => {
935+
signal.value;
936+
return <div>Hello World</div>;
937+
};
938+
`;
939+
940+
const expectedOutput = `
941+
import { useSignals as _useSignals } from "@preact/signals-react/runtime";
942+
const MyComponent = () => {
943+
var _effect = _useSignals();
944+
try {
945+
signal.value;
946+
return <div>Hello World</div>;
947+
} finally {
948+
_effect.f();
949+
}
950+
};
951+
`;
952+
953+
runTest(inputCode, expectedOutput, { mode: "all" });
954+
});
955+
});
956+
837957
describe("noTryFinally option", () => {
838958
it("prepends arrow function component with useSignals call", () => {
839959
const inputCode = `

0 commit comments

Comments
 (0)