Skip to content

Commit 7eb11e8

Browse files
authored
feat(transforms): support encrypt closure bind (#896)
- part of #881 ## todo - [x] implement transform - [ ] add example #897 - [ ] no-op encode/decode - [ ] flight serialize without key - [ ] flight serialize with key
1 parent 5c037c1 commit 7eb11e8

3 files changed

Lines changed: 77 additions & 3 deletions

File tree

packages/transforms/src/hoist.test.ts

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,14 @@ import { transformHoistInlineDirective } from "./hoist";
44
import { debugSourceMap } from "./test-utils";
55

66
describe(transformHoistInlineDirective, () => {
7-
async function testTransform(input: string) {
7+
async function testTransform(input: string, options?: { encode?: boolean }) {
88
const ast = await parseAstAsync(input);
99
const { output } = transformHoistInlineDirective(input, ast, {
1010
runtime: (value, name) =>
1111
`$$register(${value}, "<id>", ${JSON.stringify(name)})`,
1212
directive: "use server",
13+
encode: options?.encode ? (v) => `__enc(${v})` : undefined,
14+
decode: options?.encode ? (v) => `__dec(${v})` : undefined,
1315
});
1416
if (!output.hasChanged()) {
1517
return;
@@ -84,6 +86,11 @@ export default function w() {
8486
/* #__PURE__ */ Object.defineProperty($$hoist_2_w, "name", { value: "w" });
8587
"
8688
`);
89+
90+
// nothing to encode
91+
expect(await testTransform(input, { encode: true })).toBe(
92+
await testTransform(input),
93+
);
8794
});
8895

8996
it("closure", async () => {
@@ -207,6 +214,28 @@ function Counter() {
207214
/* #__PURE__ */ Object.defineProperty($$hoist_0_anonymous_server_function, "name", { value: "anonymous_server_function" });
208215
"
209216
`);
217+
218+
expect(await testTransform(input, { encode: true })).toMatchInlineSnapshot(`
219+
"
220+
let count = 0;
221+
222+
function Counter() {
223+
const name = "value";
224+
225+
return {
226+
type: "form",
227+
action: /* #__PURE__ */ $$register($$hoist_0_anonymous_server_function, "<id>", "$$hoist_0_anonymous_server_function").bind(null, __enc([name]))
228+
}
229+
}
230+
231+
;export function $$hoist_0_anonymous_server_function($$hoist_encoded, formData) {
232+
const [name] = __dec($$hoist_encoded);
233+
"use server";
234+
count += Number(formData.get(name));
235+
};
236+
/* #__PURE__ */ Object.defineProperty($$hoist_0_anonymous_server_function, "name", { value: "anonymous_server_function" });
237+
"
238+
`);
210239
});
211240

212241
it("higher order", async () => {
@@ -252,5 +281,32 @@ function validator(action) {
252281
/* #__PURE__ */ Object.defineProperty($$hoist_1_anonymous_server_function, "name", { value: "anonymous_server_function" });
253282
"
254283
`);
284+
285+
expect(await testTransform(input, { encode: true })).toMatchInlineSnapshot(`
286+
"
287+
export default function Page() {
288+
const x = 0;
289+
const action = validator(/* #__PURE__ */ $$register($$hoist_0_anonymous_server_function, "<id>", "$$hoist_0_anonymous_server_function").bind(null, __enc([x])))
290+
}
291+
292+
function validator(action) {
293+
return /* #__PURE__ */ $$register($$hoist_1_anonymous_server_function, "<id>", "$$hoist_1_anonymous_server_function").bind(null, __enc([action]));
294+
}
295+
296+
;export async function $$hoist_0_anonymous_server_function($$hoist_encoded, y) {
297+
const [x] = __dec($$hoist_encoded);
298+
"use server";
299+
return x + y;
300+
};
301+
/* #__PURE__ */ Object.defineProperty($$hoist_0_anonymous_server_function, "name", { value: "anonymous_server_function" });
302+
303+
;export async function $$hoist_1_anonymous_server_function($$hoist_encoded, arg) {
304+
const [action] = __dec($$hoist_encoded);
305+
"use server";
306+
return action(arg);
307+
};
308+
/* #__PURE__ */ Object.defineProperty($$hoist_1_anonymous_server_function, "name", { value: "anonymous_server_function" });
309+
"
310+
`);
255311
});
256312
});

packages/transforms/src/hoist.ts

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,13 @@ export function transformHoistInlineDirective(
1212
runtime,
1313
directive,
1414
rejectNonAsyncFunction,
15+
...options
1516
}: {
1617
runtime: (value: string, name: string) => string;
1718
directive: string;
1819
rejectNonAsyncFunction?: boolean;
20+
encode?: (value: string) => string;
21+
decode?: (value: string) => string;
1922
},
2023
) {
2124
const output = new MagicString(input);
@@ -59,10 +62,20 @@ export function transformHoistInlineDirective(
5962
const owner = scope.find_owner(ref);
6063
return owner && owner !== scope && owner !== analyzed.scope;
6164
});
62-
const newParams = [
65+
let newParams = [
6366
...bindVars,
6467
...node.params.map((n) => input.slice(n.start, n.end)),
6568
].join(", ");
69+
if (bindVars.length > 0 && options.decode) {
70+
newParams = [
71+
"$$hoist_encoded",
72+
...node.params.map((n) => input.slice(n.start, n.end)),
73+
].join(", ");
74+
output.appendLeft(
75+
node.body.body[0]!.start,
76+
`const [${bindVars.join(",")}] = ${options.decode("$$hoist_encoded")};\n`,
77+
);
78+
}
6679

6780
// append a new `FunctionDeclaration` at the end
6881
const newName =
@@ -82,7 +95,10 @@ export function transformHoistInlineDirective(
8295
// replace original declartion with action register + bind
8396
let newCode = `/* #__PURE__ */ ${runtime(newName, newName)}`;
8497
if (bindVars.length > 0) {
85-
newCode = `${newCode}.bind(${["null", ...bindVars].join(", ")})`;
98+
const bindArgs = options.encode
99+
? options.encode("[" + bindVars.join(", ") + "]")
100+
: bindVars.join(", ");
101+
newCode = `${newCode}.bind(null, ${bindArgs})`;
86102
}
87103
if (declName) {
88104
newCode = `const ${declName} = ${newCode};`;

packages/transforms/src/server-action.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ export function transformServerActionServer(
1212
options: {
1313
runtime: (value: string, name: string) => string;
1414
rejectNonAsyncFunction?: boolean;
15+
encode?: (value: string) => string;
16+
decode?: (value: string) => string;
1517
},
1618
) {
1719
// TODO: unify (generalize transformHoistInlineDirective to support top leve directive case)

0 commit comments

Comments
 (0)