Skip to content

Commit

Permalink
Add an "all" mode to the react transform (#421)
Browse files Browse the repository at this point in the history
"All" mode transforms all components to be reactive to signals. You can still opt-out using `/** @notrackSignals */`.

Related: #412
  • Loading branch information
andrewiggins committed Sep 29, 2023
1 parent ad6305c commit f80b251
Show file tree
Hide file tree
Showing 3 changed files with 131 additions and 1 deletion.
5 changes: 5 additions & 0 deletions .changeset/proud-dogs-study.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@preact/signals-react-transform": patch
---

Add an "all" mode to the react transform that transforms all components to be reactive to signals
7 changes: 6 additions & 1 deletion packages/react-transform/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,10 @@ function shouldTransform(
// Opt-in opts in to transformation regardless of mode
if (isOptedIntoSignalTracking(path)) return true;

if (options.mode === "all") {
return isComponentFunction(path);
}

if (options.mode == null || options.mode === "auto") {
return (isComponentFunction(path) || isCustomHook(path))
&& getData(path.scope, maybeUsesSignal) === true // Function appears to use signals;
Expand Down Expand Up @@ -311,8 +315,9 @@ export interface PluginOptions {
* Specify the mode to use:
* - `auto`: Automatically wrap all components that use signals.
* - `manual`: Only wrap components that are annotated with `@trackSignals` in a JSX comment.
* - `all`: Makes all components reactive to signals.
*/
mode?: "auto" | "manual";
mode?: "auto" | "manual" | "all";
/** Specify a custom package to import the `useSignals` hook from. */
importSource?: string;
experimental?: {
Expand Down
120 changes: 120 additions & 0 deletions packages/react-transform/test/node/index.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -834,6 +834,126 @@ describe("React Signals Babel Transform", () => {
});
});

describe("all mode transformations", () => {
it("skips transforming arrow function component with leading opt-out JSDoc comment before variable declaration", () => {
const inputCode = `
/** @noTrackSignals */
const MyComponent = () => {
return <div>{signal.value}</div>;
};
`;

const expectedOutput = inputCode;

runTest(inputCode, expectedOutput, { mode: "all" });
});

it("skips transforming function declaration components with leading opt-out JSDoc comment", () => {
const inputCode = `
/** @noTrackSignals */
function MyComponent() {
return <div>{signal.value}</div>;
}
`;

const expectedOutput = inputCode;

runTest(inputCode, expectedOutput, { mode: "all" });
});

it("transforms function declaration component that doesn't use signals", () => {
const inputCode = `
function MyComponent() {
return <div>Hello World</div>;
}
`;

const expectedOutput = `
import { useSignals as _useSignals } from "@preact/signals-react/runtime";
function MyComponent() {
var _effect = _useSignals();
try {
return <div>Hello World</div>;
} finally {
_effect.f();
}
}
`;

runTest(inputCode, expectedOutput, { mode: "all" });
});

it("transforms arrow function component with return statement that doesn't use signals", () => {
const inputCode = `
const MyComponent = () => {
return <div>Hello World</div>;
};
`;

const expectedOutput = `
import { useSignals as _useSignals } from "@preact/signals-react/runtime";
const MyComponent = () => {
var _effect = _useSignals();
try {
return <div>Hello World</div>;
} finally {
_effect.f();
}
};
`;

runTest(inputCode, expectedOutput, { mode: "all" });
});

it("transforms function declaration component that uses signals", () => {
const inputCode = `
function MyComponent() {
signal.value;
return <div>Hello World</div>;
}
`;

const expectedOutput = `
import { useSignals as _useSignals } from "@preact/signals-react/runtime";
function MyComponent() {
var _effect = _useSignals();
try {
signal.value;
return <div>Hello World</div>;
} finally {
_effect.f();
}
}
`;

runTest(inputCode, expectedOutput, { mode: "all" });
});

it("transforms arrow function component with return statement that uses signals", () => {
const inputCode = `
const MyComponent = () => {
signal.value;
return <div>Hello World</div>;
};
`;

const expectedOutput = `
import { useSignals as _useSignals } from "@preact/signals-react/runtime";
const MyComponent = () => {
var _effect = _useSignals();
try {
signal.value;
return <div>Hello World</div>;
} finally {
_effect.f();
}
};
`;

runTest(inputCode, expectedOutput, { mode: "all" });
});
});

describe("noTryFinally option", () => {
it("prepends arrow function component with useSignals call", () => {
const inputCode = `
Expand Down

0 comments on commit f80b251

Please sign in to comment.